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


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
01! Використовуючи 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.