Цілий квадратний корінь цілого числа [закритий]


12

Проблема:

На свій вибір мови напишіть найкоротшу функцію, яка повертає підлогу квадратного кореня неподписаного 64-бітного цілого числа.

Тестові приклади:

Ваша функція повинна працювати правильно для всіх входів, але ось декілька, які допомагають проілюструвати ідею:

               INPUT ⟶ OUTPUT

                   0 ⟶  0
                   1 ⟶  1
                   2 ⟶  1
                   3 ⟶  1
                   4 ⟶  2
                   8 ⟶  2
                   9 ⟶  3
                  15 ⟶  3
                  16 ⟶  4
               65535 ⟶ 255
               65536 ⟶ 256
18446744073709551615 ⟶ 4294967295

Правила:

  1. Ви можете назвати свою функцію всім, що вам подобається. (Безіменні, анонімні або лямбда-функції є нормальними, якщо вони якимось чином називаються.)
  2. Кількість персонажів - це найважливіше в цьому виклику, але час виконання також важливий. Я впевнений, що ви могли б ітераційно сканувати відповідь за час O (On) з дуже малим числом символів, але час O (log (n)) було б дійсно краще (тобто, якщо припустити вхідне значення n, не бітна довжина n).
  3. Ви, ймовірно, захочете реалізувати функцію за допомогою чисто цілої та / або булевої арифметики. Однак якщо ви дійсно хочете використовувати обчислення з плаваючою комою, то це нормально, доки ви не вимагаєте функцій бібліотеки. Отже, просто сказати return (n>0)?(uint32_t)sqrtl(n):-1;в C поза межами, хоча це дасть правильний результат. Якщо ви використовуєте арифметику з плаваючою точкою, ви можете використовувати *, /, +, -і зведення в ступінь (наприклад, **чи , ^якщо це вбудований оператор в обраному вами мовою, але тільки експоненцірованіе повноважень не менше 1 ). Це обмеження полягає у запобіганні "обману" шляхом виклику sqrt()або варіанту або підвищення значення до ½ потужності.
  4. Якщо ви використовуєте операції з плаваючою комою (див. №3), вам не потрібно, щоб тип повернення був цілим; тільки те, що повернене значення є цілим числом, наприклад, floor (sqrt (n)), і матиме змогу утримувати будь-яке непідписане 32-бітове значення.
  5. Якщо ви використовуєте C / C ++, ви можете припустити існування непідписаних 64-бітних і 32-бітних цілочисельних типів, наприклад, uint64_tі uint32_tяк визначено в stdint.h. В іншому випадку просто переконайтеся, що ваш цілочисельний тип здатний містити будь-яке 64-бітне ціле число без підпису.
  6. Якщо ваш мовний текст не підтримує 64-бітні цілі числа (наприклад, Brainfuck, мабуть, має лише 8-бітну цілочисельну підтримку), тоді зробіть все можливе з цим і вкажіть обмеження у назві відповіді. Це означає, що якщо ви можете зрозуміти, як кодувати 64-бітове ціле число і правильно отримати квадратний корінь його за допомогою 8-бітової примітивної арифметики, тоді вам більше сили!
  7. Веселіться та займайтеся творчістю!

7
"але час O (log₄ (n)) справді буде кращим." - наскільки краще? Чи є бонус? Це важка вимога? Це по суті окремий виклик? Це просто приємна ідея, яка насправді не впливає на бал?
— Джон Дворак

3
Зазвичай для отримання алгоритмічної складності використовується розмір вхідного, а не вхідного значення . У цьому сенсі алгоритм збільшення та повторної спроби експоненціальний у швидкості.
— Джон Дворак

3
Ммм ... O(log_2 n) === O(log_4 n). log_4(n) = log_2(n) / log_2(2) = log_2(n) / 2
— Джон Дворак

1
2/4 рахується?
— Міло

1
Більшість типів даних з плаваючою комою так чи інакше не мають точності, необхідної для цього завдання. 53 значущих біта недостатньо для всього діапазону введення.
— user2357112 підтримує Моніку

Відповіді:


14

CJam, 17 (або 10) байт

{_1.5#\/i}

Спробуйте в Інтернеті , перевіривши тестові приклади:

[0 1 2 3 4 8 9 15 16 65535 65536 18446744073709551615]{_1.5#\/i}%N*

Він не пройде останній тестовий випадок через проблеми з округленням, але оскільки 18446744073709551615це не ціле число в CJam (це великий Integer ), ми все ще хороші, правда?

Якщо ні, наступний (і трохи довший) код виправить ці помилки:

{__1.5#\/i_2#@>-}

Не найкоротше рішення вже, але свято .

Як це працює

__    " Duplicate the integer twice. ";
1.5#  " Raise to power 1.5. Note that, since 1.5 > 1, this doesn't break the rules. ";
\     " Swap the result with the original integer. ";
/     " Divide. ";
i     " Cast to integer. ";
_2#   " Push square of a copy. ";
@     " Rotate the orginal integer on top of the stack. ";
>-    " If the square root has been rounded up, subtract 1. ";

Ха-ха! ой, гаразд, ви мене там на техніку. Я мав би сказати, що немає дробових повноважень. Але ваш код дійсно підкоряється заявленим правилам, тому я його запрошую. :)
— Тодд Леман

2
Чи CJam має децимали довільної точності для покриття всього вхідного діапазону?
— isaacg

Крім того, приємний злом використання NaN -> 0 on cast to int.
— isaacg

Відмінні ідеї, він також можуть бути представлені в J в точно такій же кількості символів: <.@%~^&1.5. Чи можу я опублікувати це як окрему відповідь (адже це в основному саме ваш порт)?
— ɐɔıʇǝɥʇuʎs

@ ɐɔıʇǝɥʇuʎs: Вперед. Але я просто зрозумів, що моє рішення може бути неправильним для великої кількості, включаючи останній тестовий випадок. На мій захист він пройшов мою перевірку лише тому, що 4294967295і 4294967296виглядають дуже схоже ...
— Денніс

10

Хаскелл, 28 26

Я вважаю, що це найкоротший запис з будь-якої мови, який не був розроблений для гольфу.

s a=[x-1|x<-[0..],x*x>a]!!0

Він називає функцію sз параметром aі повертає одне мінус перше число, квадрат якого більший за a. Працює неймовірно повільно (O (sqrt n), можливо?).


1
Чи не буде індекс списку ( [...]!!0) коротшим від головного?
— isaacg

@isaacg Так, було б. Дякую :-)
— Зак

7

Гольфскрипт, 17 символів

{).,{.*1$<},,\;(}

Я міг назвати свою функцію будь-яким способом, який мені сподобався, але я вирішив не називати її взагалі. Додайте два символи, щоб назвати його, додайте три, щоб назвати його, а не залишати його на стеці, відніміть один символ, якщо забезпечення повноцінної програми добре.

Ця гидота працює не в логарифмічний час значення вхідного сигналу, а не в O (sqrt n) час, для отримання результату потрібна велика лінійна кількість часу. Це також займає стільки місця. Абсолютно жахливий. Але ... це код-гольф.

Алгоритм:

n => [0..n].filter(x => x*x < n+1).length - 1

Я це люблю!! Хороша робота! Це прекрасно збочено.
— Тодд Леман

7

Піта , 14 символів

DsbR;fgb*TTL'b

Забезпечує названу функцію s, яка обчислює квадратний корінь, фільтруючи список від 0 до n для того, щоб квадрат був більшим за вхідний, а потім друкує останнє таке число. Не використовує експонентності та плавання.

Dsb       def s(b):
R;        return last element of
f         filter(lambda T:
gb*TT                     b>=T*T,
L'b                       range(b+1))

Приклад використання:

python3 pyth.py <<< "DsbR;fgb*TTL'b       \msd[0 1 2 3 4 8 9 15 16 65535 65536"
[0, 1, 1, 1, 2, 2, 3, 3, 4, 255, 256]

7

Сітківка (неконкурентна - мова новіша, ніж виклик), 43

Працюючи над цією відповіддю , мені спало на думку, що подібний метод можна використовувати для обчислення цілих квадратних коренів за допомогою сітківки:

.+
$*
^
1:
+`(1+):(11\1)
1 $2:
1+:$|:1+

1+

Це спирається на той факт, що досконалі квадрати можуть бути виражені як 1+3+5+7+...і, як наслідок, число доданків у цьому виразі - квадратний корінь.

Спробуйте в Інтернеті. (Додано перший рядок, щоб дозволити запуск декількох тестових корпусів.)

Очевидно, через десяткове до одинарне перетворення це буде працювати лише для відносно невеликих входів.


4
(Мова новіша, ніж виклик)
— mbomb007

@ mbomb007 Досить справедливо - Заголовок відредаговано. Ця відповідь, безумовно, належить до категорії «тому що це можна зробити», і не має на меті змагатись із викликом будь-яким змістовно.
— Цифрова травма


6

Perl, 133 символи

Не найкоротший на сьогоднішній день, але використовує алгоритм з цифрою в цифрах для обробки будь-якого введення розміру і працює в O (log n) час. Перетворює вільно між числами як рядки та числами як числа. Оскільки найбільшим можливим продуктом є корінь досі з квадратом однозначної цифри, він повинен мати можливість приймати квадратний корінь до 120-бітових чисел чисел у 64-бітній системі.

sub{($_)=@_;$_="0$_"if(length)%2;$a=$r="";while(/(..)/g){
$a.=$1;$y=$d=0;$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for 1..9;$r.=$d;$a-=$y}$r}

Декомпресований, тобто:

sub {
  my ($n) = @_;
  $n = "0$n" if length($n) % 2; # Make an even number of digits
  my ($carry, $root);
  while ($n =~ /(..)/g) { # Take digits of $n two at a time
    $carry .= $1;         # Add them to the carry
    my ($product, $digit) = (0, 0);
    # Find the largest next digit that won't overflow, using the formula
    # (10x+y)^2 = 100x^2 + 20xy + y^2 or
    # (10x+y)^2 = 100x^2 + y(20x + y)
    for my $trial_digit (1..9) {
      my $trial_product = $trial_digit * (20 * $root + $trial_digit);
      if ($trial_product <= $carry) {
        ($product, $digit) = ($trial_product, $trial_digit);
      } 
    } 
    $root .= $digit;
    $carry -= $product;
  } 
  return $root;
}

Приємно! Мені було цікаво, коли хтось опублікує відповідь Perl. BTW, чи працює це if length%2замість цього if(length)%2? Це би відрізало 1 символ. Крім того, чи вдасться сказати $y=$z,$d=$_ ifзамість цього ($y,$d)=($z,$_)if? Я думаю, що це могло б збрити ще 3 символи.
— Тодд Леман

І це стає трохи перекрученим, але я думаю, що ви можете збрити ще 1, переписавши forпетлю так:$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
— Todd Lehman

Перша пропозиція не працює (вона намагається взяти довжину названого хеша %2), але інші дійсні. Я працюю над ними.
— Хоббі

1
Постфіксу @ToddLehman forне потрібні дужки; додавши, що до ваших пропозицій сідає мені всього 6 символів. Дякую!
— варення

5

Матлаб (56) / Октава (55)

Він опрацьовує квадратний корінь, використовуючи метод фіксованої точки. Він конвергується в максимальних 36 кроків (для аргументу 2 ^ 64-1), а потім перевіряє, чи є нижчим одним із "можливих" цілих коренів. Оскільки він завжди використовує 36 ітерацій, він має час виконання O (1) = P

Аргумент вважається uint64.

Матлаб:

function x=q(s)
x=1
for i = 1:36
    x = (x+s/x)/2
end
if x*x>s
    x=x-1
end

Октава:

function x=q(s)
x=1
for i = 1:36
    x = (x+s/x)/2
end
if x*x>s
    x-=1
end

Це новий метод для мене, і він трапляється досить круто. +1
— seequ

1
Це, в основному, en.wikipedia.org/wiki/…, який є одним з найдавніших відомих чисельних методів, який, за оцінками, становить близько 3700 років. Це можна виправдати en.wikipedia.org/wiki/Banach_fixed-point_theorem, який має напрочуд легкий доказ, це дійсно приємно =)
— недолік

5

Рубін - 36 символів

s=->n{g=n;g=(g+n/g)/2 while g*g>n;g}

Чудово зроблено! Який найгірший час виконання?
— Тодд Леман

Що робити у випадку, якщо g * g <n і відповідь все ще не наближається до потрібного значення? Чи не зупиниться сценарій просто?
— WallyWest

1
@ToddLehman Я, чесно кажучи, не знаю. : - / Це вавилонський метод . Ось, що здається хорошим доказом середньої складності . Початкова здогадка про число сама по собі є досить поганою, але мені потрібно було б сісти і по-справжньому виграти цей доказ, щоб зрозуміти найгірший випадок. Подобається, коли у мене буде трохи більше вільного часу. :-)
— OI

@WallyWest Моє розуміння полягає в тому, що whileцикл закінчується саме тоді, коли g переходить на підлогу (√n), що є бажаним значенням. Чи бачите ви випадок, коли це не було би правдою?
— OI

4

Пітон (39)

f=lambda n,k=0:k*k>n and k-1or f(n,k+1)

Природний рекурсивний підхід. Підраховує потенційні квадратні корінці, поки їхня площа не зависока, а потім знижується на 1. Використовуйте Stackless Python, якщо ви переживаєте за перевищення глибини стека.

and/orІдіоми еквівалентно потрійному оператора як

f=lambda n,k=0:k-1 if k*k>n else f(n,k+1)

Edit: я можу замість цього отримати 25 символів шляхом використання правила «ви можете використовувати *, /, +, -, і зведення в ступінь (наприклад, **чи , ^якщо це вбудований оператор в обраному вами мовою, але тільки експоненцірованіе повноважень не менше 1). " (Редагувати: Мабуть, Денніс уже знайшов та скористався цим фокусом.)

lambda n:n**1.5//max(n,1)

Я використовую оператор цілочисельного поділу //Python 3 для округлення вниз. На жаль, я витрачаю дуже багато символів, щоб справа n=0не дала поділу на 0 помилок. Якби не це, я міг би зробити 18 символів

lambda n:n**1.5//n 

У правилах також не було сказано, що функцію потрібно було назвати (залежно від того, як ви інтерпретуєте "Ви можете назвати свою функцію будь-чим, що вам подобається"), але якщо це так, це ще два символи.


- Дякую, я уточню це. Це має бути лише функцією. Його не потрібно називати. Отже, лямбда-функції чудово. Я б згадав про це з самого початку, якби я подумав про це. Я надто багато думав з точки зору С, коли розміщував питання.
— Тодд Леман

4

C99 (58 символів)

Це приклад відповіді, який я б не вважав гарною, хоча мені це цікаво з точки зору коду на гольф, оскільки це так збочено, і я просто подумав, що було б весело кинути в мікс:

Оригінал: 64 символи

uint64_t r(uint64_t n){uint64_t r=1;for(;n/r/r;r++);return r-1;}

Причина цього страшна в тому, що вона працює в часі O (√n), а не O (log (n)). (Де n - вхідне значення.)

Редагування: 63 символи

Зміна r-1на --rта примикання його до return:

uint64_t r(uint64_t n){uint64_t r=1;for(;n/r/r;r++);return--r;}

Редагування: 62 символи

Переміщення приросту циклу всередині умовної частини циклу (зауважте: це має не гарантовану поведінку, оскільки порядок операцій щодо оператора попереднього підключення залежить від компілятора):

uint64_t r(uint64_t n){uint64_t r=0;for(;n/++r/r;);return--r;}

Редагування: 60 символів

Додавання typedefприховування uint64_t( заслужив користувач технозавр для цієї пропозиції).

typedef uint64_t Z;Z r(Z n){Z r=0;for(;n/++r/r;);return--r;}

Редагування: 58 символів

Тепер вимагається, щоб другий параметр був переданий як 0 у виклику функції, наприклад, r(n,0)замість просто r(n). Ок, за життя мене, на даний момент я не бачу, як далі стиснути це ... хтось?

typedef uint64_t Z;Z r(Z n,Z r){for(;n/++r/r;);return--r;}

Якщо ви готові назвати це C ++ і декремента , а не збільшення ви могли б збрити пару символів: uint64_t s(uint64_t n){for(uint64_t r=n;--n>r/n;);return n;}.
— Форс

@Fors - Гарний підхід! На жаль, чи не спричинить це ділення на нуль для введення 1? Крім того, що це зробить для введення 0? Тому що --nколи n==0було б –1, а це непідписані значення, то –1 було б 2⁶⁴ – 1.
— Тодд Леман

1
#define Z uint64_t ... або typedef врятує пару
— технозавр

@technosaurus - Так, це економить 2. Дякую. :-)
— Тодд Леман

1
Вираз n/++r/rмає невизначену поведінку ....
— aschepler

4

Гольфскрипт - 14 символів

{.,\{\.*<}+?(}

Знайдіть найменше число, iменше за вхід, nдля якого n < i*i. Повернення i - 1.

Тобто [0..n-1].first(i => n < i*i) - 1

Пояснення для тих, хто також не знає Golfscript, для зразкового виклику з введенням 5:

.        //Duplicate input.  Stack: 5 5
,        //Get array less than top of stack.  Stack: 5 [0 1 2 3 4]
\        //Switch top two elements of stack.  Stack: [0 1 2 3 4] 5
{\.*<}+  //Create a block (to be explained), and prepend the top of the stack.  
         //Stack: [0 1 2 3 4]{5\.*<}
?        //Find the first element of the array for which the block is true. 
         //So, find the first element of [0 1 2 3 4] for which {5\.*<} evaluates to true.
         //The inner block squares a number and returns true if it is greater than the input.
(        //Decrement by 1 

О, це на 3 символи коротше попередньої найкращої відповіді Гольфскрипту. Хороша робота!
— Тодд Леман

Виправлення цього, щоб дати правильну відповідь для введення, 1ймовірно, потребує двох знаків.
— Пітер Тейлор

4

Haskell, 147 138 134 128 байт

Не найкоротший код у світі, але він працює в O (log n) і на довільних розмірах:

h x=div(x+1)2
n%(g,s)|g*g<n=(g+s,h s)|g*g>n=(g-s,h s)|0<1=(g,0)
f(x:r@(y:z:w))|x==z=min x y|0<1=f r
s n=fst$f$iterate(n%)(n,h n)

Це робить двійковий пошук діапазону [0..n], щоб знайти найкраще нижнє наближення до sqrt (n). Ось незворушена версія:

-- Perform integer division by 2, rounding up
half x = x `div` 2 + x `rem` 2

-- Given a guess and step size, refine the guess by adding 
-- or subtracting the step as needed.  Return the new guess
-- and step size; if we found the square root exactly, set
-- the new step size to 0.
refineGuess n (guess, step)
    | square < n  =  (guess + step, half step)
    | square > n  =  (guess - step, half step)
    | otherwise   =  (guess, 0)
    where square = guess * guess     

-- Begin with the guess sqrt(n) = n and step size (half n),
-- then generate the infinite sequence of refined guesses.
-- 
-- NOTE: The sequence of guesses will do one of two things:
--         - If n has an integral square root m, the guess 
--           sequence will eventually be m,m,m,...
--         - If n does not have an exact integral square root,
--           the guess sequence will eventually alternate
--           L,U,L,U,.. between the integral lower and upper
--           bounds of the true square root.
--        In either case, the sequence will reach periodic
--        behavior in O(log n) iterations.
guesses n = map fst $ iterate (refineGuess n) (n, half n)

-- Find the limiting behavior of the guess sequence and pick out
-- the lower bound (either L or m in the comments above)
isqrt n = min2Cycle (guesses n)
    where min2Cycle (x0:rest@(x1:x2:xs))
            | x0 == x2    =   min x0 x1
            | otherwise   =   min2Cycle rest

Редагувати: збережено два байти, замінивши пункти "в іншому випадку" на "0 <1" як скорочену версію "True" та ще кілька, включивши g * g.

Крім того, якщо ви задоволені O (sqrt (n)), ви можете просто зробити

s n=(head$filter((>n).(^2))[0..])-1

на 35 символів, але що це весело?

Редагування 2: Я щойно зрозумів, що оскільки пари сортуються за порядком словника, а не роблять min2Cycle. карта fst, я можу просто зробити fst. min2 Цикл. У коді для гольфу, що перекладається на заміну f $ map fst на fst $ f, економлячи ще 4 байти.

Редагувати 3: Збережено ще шість байтів завдяки ponoshaskeller!


1
ви можете замінити (div x 2 + rem x 2) на div (x + 1) 2, у вашій функції "половина"
— гордий haskeller

Насправді у мене є власне рішення, яке містить 49 символів, і вирішує O (log n), але у мене є лише 2 оновлення ;-(. Я не розумію, чому
— гордий haskeller

4

JavaScript 91 88 86: Оптимізовано для швидкості

function s(n){var a=1,b=n;while(Math.abs(a-b)>1){b=n/a;a=(a+b)/2}return Math.floor(a)}

JavaScript 46: Не оптимізовано для швидкості

function s(n){a=1;while(a*a<=n)a++;return a-1}

Ось JSFiddle: http://jsfiddle.net/rmadhuram/1Lnjuo4k/


1
Ласкаво просимо до PPCG! Ви можете використовувати <s> 91 </s> <s> 88 </s> для прориву. Я спробував внести правки, але ви одночасно редагували, тому я дозволяю вам це зробити.
— Rainbolt

1
Або ви могли зробити це в 41 символі, як це:function s(n){for(a=1;++a*a<n;);return a}
— Rhubarb Custard,

4

C 95 97

Редагувати Typedef, запропоновану @Michaelangelo

Це має бути більш-менш простою реалізацією алгоритму Герона. Єдина хитрість полягає в обчисленні середнього уникнення переливу цілих чисел: a = (m + n) / 2 не працює для чисел бііііг.

typedef uint64_t Z;
Z q(Z x)
{
   Z n=1,a=x,m=0;
   for(;a-m&&a-n;) n=a,m=x/n,a=m/2+n/2+(m&n&1);
   return a;
}

Приємна робота з уникненням переливу - не тільки для того, щоб правильно це зробити, але й подбати про те, щоб продумати це в першу чергу і перевірити. Однозначно цінується.
— Тодд Леман

До речі, дивно, наскільки дорогим може бути поділ на деяких процесорах. Хоча цей алгоритм виконує приблизно вдвічі більше кроків, ніж алгоритм абакуса, він має час виконання, який приблизно в 5 разів повільніше алгоритму абаку, коли я орієнтую його на моєму процесорі Core i7, що не любить робити поділ. Так чи інакше, але час виконання тут не важливий - лише розмір. :) Так гарна робота !!!
— Тодд Леман

4

C # 64 62 55

Оскільки це (і мені страшно з математикою), а час виконання - лише пропозиція, я здійснив наївний підхід, який працює в лінійний час:

decimal f(ulong a){var i=0m;while(++i*i<=a);return--i;}

( тест на dotnetfiddle )

Звичайно, для великих входів страшенно повільно.


1
Можливо, ви зможете поголити персонажа, змінивши return i-1на return--i?
— Тодд Леман

У виразі i*i<=a, чи гарантується це ціла арифметика звичайного типу? (Я не знайомий з C #.) Якщо так, і якщо C # дозволяє неявне ціле перетворення в булеве, як це робить C, то, можливо, ви зможете зберегти ще один символ, змінивши його на a/i/i.
— Тодд Леман

1
@ToddLehman Це фактично трапляється арифметикою з фіксованою точкою ( Decimalбільш високе значення максимуму та точність), щоб уникнути переповнення, оскільки результат множення потенційно може пройти на крок вперед UInt64.MaxValue. Але C # так чи інакше не має явних перетворень на Boolean. Я повинен мати можливість змінити return, дякую. Я зроблю це, коли повернусь до комп’ютера.
— Боб

3

Clojure - 51 або 55 байт

Перевіряє всі числа від n до 0, даючи перше куди x^2 <= n. ВиконанняO(n - sqrt n)

Без назви:

(fn[x](first(filter #(<=(* % %)x)(range x -1 -1))))

Названо:

(defn f[x](first(filter #(<=(* % %)x)(range x -1 -1))))

Приклад:

(map (fn[x](first(filter #(<=(* % %)x)(range x -1 -1)))) (range 50))
=> (0 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 7)

3

Befunge 93 - 48 байт або 38 символів

101p&02p>02g01g:*`#v_01g1-.@
        ^  p10+1g10<        

Спробуйте тут.


1
Гаразд, це просто круто. Хороша робота! Я ввійшов до 17, натиснув Creep, а потім Run, і вийшло 4! :)
— Тодд Леман

3

Кобра - 62

do(n as uint64)as uint64
    o=n-n
    while o*o<n,o+=1
    return o

Пакет - 74

set a=0
:1
set /ab=%a%*%a%
if %b% LSS %1 set /aa=%a%+1&goto 1
echo %a%

3

Haskell, 53 50 49 символів, O (журнал n)

s n=until((<=n).(^2))(\g->g-1-div(g^2-n-1)(2*g))n

це рішення реалізує метод Ньютона-Рафсона, хоча він шукає цілі числа замість плавців. wiki: http://en.wikipedia.org/wiki/Newton%27s_method

складність, здається, стосується O (log n), але чи є докази цього? будь ласка, відповідайте в коментарях.


\g->div(n+g^2)$2*gекономить 7 байт.
— Anders Kaseorg

3

J (10)

Дуже, дуже, дуже натхненно відповідь @Dennis :

<.@%~^&1.5

І трохи довше, але з кращими показниками (підозрюю):

<.@(-:&.^.)

floor(halve under log)

Для виконання вводяться відступні частини:

   f=:<.@%~^&1.5
   f 0 8 12 16
0 2 3 4
   g=:<.@(-:&.^.)
   g 0 8 12 16
0 2 3 4

Як це виконати для заданого цілого числа?
— Денніс

1
@Dennis Дивіться відповідь
— ɐɔıʇǝɥʇuʎs

3

APL - 12 символів, 19 байт

{⌊(⍵*1.5)÷⍵}

Приклад використання:

{⌊(⍵*1.5)÷⍵}17

повертає 4

Тестовий вектор

{⌊(⍵*1.5)÷⍵}¨0 1 2 3 4 8 9 15 16 65535 65536 18446744073709551615

повертає

1 1 1 1 2 2 3 3 4 255 256 4294967296

Спробуйте в Інтернеті

Велика подяка : користувачеві "ssdecontrol" за алгоритм


1
Ласкаво просимо до PPCG! Зазвичай ми оцінюємо APL як один байт на символ. Якщо виклик не визначає його, в UTF-8 рахувати не потрібно. Будь-яке існуюче кодування добре, і є стара кодова сторінка APL з того дня, яка використовує один байт для кожного символу. Те, що APL передує ASCII, є поганою причиною штрафувати його за використання символів, що не належать до ASCII. ;) (Отож, цей досить складний виклик, здається, персонажів все одно.)
— Мартін Ендер

@MartinEnder Дякую за теплий прийом та поради :)
— QuantumKarl

1
0→ 1! Використовуючи Dyalog APL , ви можете встановити ⎕DIV←1(який багато хто використовує за замовчуванням) для отримання правильного результату.
— Адам

2

C99 (108 символів)

Ось моє власне рішення C99, яке адаптоване за алгоритмом у статті у Вікіпедії . Я впевнений, що це можливо зробити набагато краще, ніж це в інших мовах.

Гольф:

uint64_t s(uint64_t n){uint64_t b=1,r=0;while(n/b/4)b*=4;for(;b;b/=4,r/=2)n>=r+b?r+=b,n-=r,r+=b:0;return r;}

Частково гольф:

uint64 uint64_sqrt(uint64 n)
{
  uint64 b = 1, r = 0;
  while (b <= n / 4)
    b *= 4;
  for (; b; b /= 4, r /= 2)
    if (n >= r + b)
      { r += b; n -= r; r+= b; }
  return r;
}

Безголівки:

uint64_t uint64_sqrt(uint64_t const n)
{
  uint64_t a, b, r;

  for (b = 1; ((b << 2) != 0) && ((b << 2) <= n); b <<= 2)
    ;

  a = n;
  r = 0;
  for (; b != 0; b >>= 2)
  {
    if (a >= r + b)
    {
      a -= r + b;
      r = (r >> 1) + b;
    }
    else
    {
      r >>= 1;
    }
  }

  // Validate that r² <= n < (r+1)², being careful to avoid integer overflow,
  // which would occur in the case where n==2⁶⁴-1, r==2³²-1, and could also
  // occur in the event that r is incorrect.
  assert(n>0? r<=n/r : r==0);  // Safe way of saying r*r <= n
  assert(n/(r+1) < (r+1));     // Safe way of saying n < (r+1)*(r+1)

  return r;
}

1
Пропозиція: Не потрібно a, використовуйте n.
— edc65

Ага так. Дякую. У моїй оригінальній версії я підтримував nтак, що безпосередньо перед поверненням я міг зробити твердження (не показано), що r ^ 2 <= n <(r + 1) ^ 2. Якщо це твердження опущено, потрібно довше залишатися nнедоторканими.
— Тодд Леман

@ edc65 - ще раз дякую, що вказав на це. Я оновив свій код, щоб відобразити це, а також додав ще кілька хитрощів. Також додали оригінальні твердження і зробили n constу версії, що не перебуває у вогонь.
— Тодд Леман

2

JavaScript 73 81 (щоб відповідати вимогам 64-бітних чисел)

n=prompt();g=n/3;do{G=g,g=(n/g+g)/2}while(1E-9<Math.abs(G-g))alert(Math.floor(g))

Реалізація алгоритму Герої Олександрії ...


Приємно! Чи працює це для всіх непідписаних 64-бітних цілих входів?
— Тодд Леман

Спробуйте, як я можу, це працює лише до 32-розрядних ... На мій розчарування ...
— WallyWest

Безумовно, останній | 0 скорочує будь-яке значення до 32 біта. Використовуючи Math.floor замість цього?
— edc65

@ edc65 Ти маєш рацію насправді, здається, |0впливає на 32-розрядні, тоді як Math.floorвін більш ефективний у 64-бітовій версії ... Я оновив свій код, для цього потрібно взяти ще 8 символів
— WallyWest

@ edc65 У мене просто була думка ... чи буде ~~ x працювати в 64-бітному?
— WallyWest

2

Powershell (52) Обмежено до Int32 (-2,147,483,648 до 2,147,483,647)

function f($n){($n/2)..0|%{if($_*$_-le$n){$_;exit}}}

Я зараз кричу на Пауершелла, намагаючись зробити останній тестовий випадок, але незалежно від того, що я роблю, Пауершелл закінчується, використовуючи змінну конвеєра $ _ як Int32, і зараз я не можу знайти шлях.

Тож я зараз обмежу свою відповідь. Якщо я зможу знайти кращий спосіб попрацювати з uint64s, я відредагую. (Останній тестовий випадок, до речі, занадто великий для нормального типу Int64 Powershell!)

Ось кілька тестових випадків (з додатковим результатом, який я використовував для відстеження часу)

f 17
4
Elapsed Time: 0.0060006 seconds

f 65
8
Elapsed Time: 0.0050005 seconds

f 65540
256
Elapsed Time: 1.7931793 seconds

f 256554
506
Elapsed Time: 14.7395391 seconds

Я не знаю своїх O (), але це здається досить драматичним стрибком.


2

Caveat: станом на 2011 рік R не мала вбудованої підтримки для 64-бітових цілих чисел, як я вважав, що це робив. Ці відповіді можуть бути недійсними щодо цієї технічності, але потім R знову змінився за останні 3 роки.


R, 85

Використовуючи метод Ньютона:

function(n){s=F
x=n
y=(1/2)*(x+n/x)
while(abs(x-y)>=1){x=y
y=(1/2)*(x+n/x)}
trunc(y)}

яка сходиться квадратично. +2 символи, щоб призначити функцію змінної для тестування:

microbenchmark(q(113424534523616))
# Unit: microseconds
#                expr    min      lq median      uq    max neval
#  q(113424534523616) 24.489 25.9935 28.162 29.5755 46.192   100

R, 37

Груба сила:

function(n){t=0
while(t^2<n) t=t+1
t}

І та сама перевірка:

microbenchmark::microbenchmark(q(113424534523616),times=1)
# Unit: seconds
#                 expr      min       lq   median       uq      max neval
#   q(113424534523616) 4.578494 4.578494 4.578494 4.578494 4.578494     1

R, 30

Дешево / блискучий експоненцірованіе трюк :

function(n) trunc(n^(1.5)/n)

що також буває дуже швидким (хоча не таким швидким, як вбудований):

microbenchmark(q(113424534523616),sqrt(113424534523616))
# Unit: nanoseconds
#                   expr min    lq median    uq  max neval
#     z(113424534523616) 468 622.5  676.5 714.5 4067   100
#  sqrt(113424534523616)  93 101.0  119.0 160.5 2863   100

2

С, 38

f(n){int m;while(++m*m<=n);return--m;}

Переклад мого четвертого подання. Повільно, але правильно. O (√n). Тестовано на OS X (64 біт).


2

постійного струму, 50 байт

dc -e"?dsist[lt2/dstd*li<B]dsBx[lt1+dstd*li!<A]dsAxlt1-f"

Відставив і пояснив:

               # The idea here is to start with the input and reduce it quickly until it is
               # less than what we want, then increment it until it's just right
?              # Take input from stdin
d si st        # Duplicate input, store in `i' and in `t'
[              # Begin macro definition (when I write in dc, "macro"=="function")
 lt            # Load t, our test term
 2/            # Divide t by two
 d st          # Store a copy of this new term in `t'
 d*            # Duplicate and multiply (square)
 li<B          # Load i; if i<(t^2), execute B
] d sB x       # Duplicate, store function as `B', and execute
               # Loop ends when t^2 is less than i
[              # Begin macro definition
 lt            # Load t, our test term
 1+            # Increment
 d st          # Store a copy of this new term in `t'
 d*            # Duplicate and multiply (square)
 li!<A         # Load i; if i>=(t^2), execute A
] d sA x       # Duplicate, store function as `A', and execute
               # Loop ends when t^2 == i+1
lt 1- f        # Load t, decrement, and dump stack

А, схоже, випадає останній тестовий випадок. Я спробую це виправити.
— Джо

Вирішено. Тепер приймає дуже великий вклад; Насправді, виправлення дозволило мені видалити якийсь некрасивий код на початку.
— Джо

2

C, 139 137 136 байт

Перша моя спроба в коді гольфу. Схоже, це найкоротший у С, який відповідає "ефективній" вимозі, оскільки він працює в O(log n)часі, використовуючи лише додавання та зсуви бітів. Хоча я впевнений, що це може бути ще коротшим ...

Він повинен спрацьовувати чудово і для більших цілих значень, доки a=32частина не буде змінена a=NUMBITS/2.

typedef uint64_t x;x f(x o){x a=32,t=0,r=0,y=0,z;for(;a--+1;){z=(x)3<<2*a;y*=2;t++<r?y++,r-=t++:t--;t*=2;r*=4;r+=(o&z)>>2*a;}return y;}

Хороша робота! Я не запускав його для тестування, але код виглядає цікаво. Чи є причина, про яку ви писали, (t++)а не просто t++в дорученні r?
— Тодд Леман

1
@ToddLehman Nope, просто пропустив їх. Приємний улов!
— Кріс

До речі, я люблюa--+1 як спосіб уникнути написання a-- != UINT64_C(-1). Ви навчилися цього трюку десь або винайшли його самі?
— Тодд Леман

1
@ToddLehman Дякую! Я зрозумів це сам.
— Кріс

1

C - 50 (61 без глобальної)

typedef uint64_t T;T n,i;f(){while(++i*i<=n);--i;}

Він використовує глобальні змінні як параметр і повертає значення для економії місця.

Немає глобальної версії:

typedef uint64_t T;T f(T n){T i=0;while(++i*i<=n);return--i;}

1
Я не думаю, що використання глобальних змінних є законним. Принаймні розкажіть, як довго це було б законно, і надайте законну версію
— гордий haskeller

@proud haskeller Чому глобальні змінні будуть заборонені?
— mantale

@mantal, оскільки ви повинні надати програму / метод, що можна виконати.
— Marciano.Andrade

@ Marciano.Andrade код надається можна виконати.
— mantale

1

C ++ 125

int main()
{
uint64_t y;cin>>y;
double x=y/2,d,z;
while((d=(x*x-y))>0.5)
{
d<0?x+=0.5:x-=0.5;
}
cout<<(uint64_t)x;
}

Приємно! Як щодо x+=(d<0)-0.5;... економить ще 5 символів?
— Тодд Леман

До речі, це не (але повинно бути) у вигляді функції, як зазначено в заяві проблеми. (Гаразд, технічно, так, mainце функція, але її не можна телефонувати всередині програми, як це f(y)було б.)
— Todd Lehman

Я думаю, ви можете опустити найпотаємнішу пару дужок і написати while((d=x*x-y)>0.5)замість while((d=(x*x-y))>0.5). Зберігає ще 2 символи. :)
— Тодд Леман

Кожні 0,5 можна змінити на .5
— Yytsi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.