整数的整数平方根[关闭]


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. 您可以随意命名函数。(未命名,匿名或lambda函数可以,只要可以调用即可。)
  2. 字符计数是此挑战中最重要的,但是运行时也很重要。我敢肯定,您可以使用很少的字符数在O(√n)时间内迭代向上搜索答案,但是O(log(n))时间确实更好(也就是说,假设输入值为n,不是n的位长)。
  3. 您可能会想使用纯整数和/或布尔算术来实现该功能。但是,如果您确实要使用浮点计算,那么只要您不调用任何库函数就可以了。因此,简单地说return (n>0)?(uint32_t)sqrtl(n):-1;C会超出范围,即使它会产生正确的结果。如果您使用浮点运算,您可以使用*/+-,和幂(例如,**或者^如果它是一个内置在你选择的语言操作,但权力的唯一幂比1少)。此限制是为了防止通过调用sqrt()或变种或将值提高到1/2幂来“作弊” 。
  4. 如果使用浮点运算(请参阅#3),则不需要返回类型为整数;只是返回值是整数,例如floor(sqrt(n)),并且能够保存任何无符号的32位值。
  5. 如果您使用的是C / C ++,则可以假定存在无符号的64位和32位整数类型,例如uint64_tuint32_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算吗?
米洛2014年

1
无论如何,大多数浮点数据类型都不具有此任务所需的精度。53个有效位不足以覆盖整个输入范围。
user2357112支持Monica 2014年

Answers:


14

CJam,17(或10)个字节

{_1.5#\/i}

通过验证测试用例在线尝试

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

由于四舍五入问题,它不会通过最后一个测试用例,但是由于CJam中18446744073709551615不是Integer(这是Big Integer),所以我们还是不错的,对吗?

如果不是,则以下(且稍长一些)的代码将纠正这些错误:

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

不再是最短的解决方案了,而是faaast

怎么运行的

__    " 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. ";

哈哈哈!糟糕,您了解我的地方了。我应该没有分数权力。但是您的代码确实符合规定的规则,因此我对此表示反对。:)
Todd Lehman 2014年

2
CJam是否具有任意精度的小数来覆盖整个输入范围?
isaacg 2014年

另外,在转换为int时使用NaN-> 0的不错技巧。
isaacg 2014年

这很简单,也可以用完全相同的字符数在J中表示<.@%~^&1.5。我可以将其作为单独的答案发布(因为它基本上是您的确切端口)?
ɐɔıʇǝɥʇuʎs

@ɐɔıʇǝɥʇuʎs:继续。但是我只是发现我的解决方案可能无法正确处理大数字,包括最后一个测试用例。在我的辩护中,它通过了我的检查只是因为4294967295并且4294967296看起来非常相似...
丹尼斯

10

哈斯克尔,28 26

我认为这是所有非高尔夫语言中最短的条目。

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

s使用参数命名一个函数,a并返回一个减去第一个大于2的平方的数字a。运行非常慢(可能是O(sqrt n)?)。


1
列表索引([...]!!0)会比头短吗?
isaacg 2014年

@isaacg是的,它将。谢谢:-)
Zaq 2014年

7

Golfscript,17个字符

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

我可以用自己喜欢的任何方式来命名函数,但是我决定完全不命名它。添加两个字符来命名,添加三个名称而不是将其保留在堆栈中,如果提供完整的程序是可以的,则减去一个字符。

这种可憎性不是以对数时间以输入值的形式运行,也不是以O(sqrt n)的时间运行,要花费大量的线性时间才能得出结果。它也占用那么多空间。绝对可怕。但是...这是代码高尔夫。

该算法是:

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

我喜欢它!!干得好!那真是不合常理。
托德·雷曼

7

Pyth,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 '16

@ mbomb007足够公平-标题已编辑。这个答案肯定属于“因为它可以做到”类别,并不意味着以任何有意义的方式参与挑战。
Digital Trauma


6

Perl,133个字符

到目前为止,它不是最短的,但是使用数位算法来处理任何大小的输入,并且运行时间为O(log n)。在数字字符串和数字数字之间自由转换。由于最大的乘积是具有一位数平方的平方根,因此在64位系统上,它应该能够采用最大120位左右的数字的平方根。

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答案。顺便说一句,说if length%2而不是工作if(length)%2吗?那会刮掉1个字符。另外,说$y=$z,$d=$_ if代替它会有用($y,$d)=($z,$_)if吗?我认为这样可以减少3个字符。
托德·雷曼

这有点不正常,但是我认为您可以通过将for循环重写为以下方式来减少1 :$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
Todd Lehman

第一条建议不起作用(它尝试采用名为的哈希值的长度%2),但其他建议有效。我会解决它们。
霍布斯

1
@ToddLehman后缀for不需要括号;将其添加到您的建议中可以使我总共获得6个字符。谢谢!
hobbs

5

Matlab(56)/八度(55)

它使用定点方法算出平方根。它以最大36步收敛(对于2 ^ 64-1作为参数),然后检查它是否是“可能的”整数根的下一个。由于它总是使用36次迭代,因此其运行时间为O(1)= P

假定参数为uint64。

Matlab:

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 2014年

1
它基本上是en.wikipedia.org/wiki/…,这是最早的已知数值方法之一,估计已有3700年的历史了。可以通过en.wikipedia.org/wiki/Banach_fixed-point_theorem证明其合理,该证明出奇的简单证明,它非常好=)
虚假的2014年

5

Ruby-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收敛到所需值floor(√n)时,循环恰好终止。您是否看到不符合要求的情况?
OI 2014年

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)

编辑:我可以改为获得25个字符通过利用规则“,你可以使用*/+-,和幂(例如,**或者^如果它是权力的一个内置在你选择的语言操作,但只幂比1以下)。 ” (编辑:显然,丹尼斯已经发现并利用了这个技巧。)

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

我使用//Python 3 的整数除法运算符四舍五入。不幸的是,我花了很多字符,以免n=0错误不除以0。如果不是,我可以做18个字符

lambda n:n**1.5//n 

规则也没有说必须对函数进行命名(取决于您如何解释“您可以按自己的喜好命名函数”。),但是如果这样,那就是另外两个字符。


—谢谢,我会澄清这一点。它仅是一个函数。它不必命名。因此,lambda函数很好。如果我想到的话,我会从一开始就提到它。发布问题时,我对C的思考过多。
托德·雷曼

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个字符

将循环增量移到循环的条件部分内部(注意:这具有无法保证的行为,因为相对于preincrement运算符的操作顺序是特定于编译器的):

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)而不是just 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 2014年

@Fors —不错的方法!不幸的是,这不会导致输入1除以零吗?另外,输入0会做什么?因为--nwhen n==0是–1,并且这些都是无符号值,所以–1将是2⁶⁴–1。
托德·雷曼

1
#define Z uint64_t ...或typedef可以拯救一对夫妇
龙2014年

@technosaurus —好的,可以节省2。谢谢。:-)
托德·雷曼

1
表达n/++r/r具有未定义的行为....
aschepler

4

Golfscript-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 

哦,这比以前的最佳Golfscript答案短了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

编辑:通过将“ otherwise”子句替换为“ True”的较短版本,将“ otherwise”子句替换为“ 0 <1”,从而节省了两个字节,并通过内联g * g节省了更多的字节。

另外,如果您对O(sqrt(n))感到满意,则只需

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

35个字符,但这有什么乐趣?

编辑2:我只是意识到,由于对是按字典顺序排序的,而不是执行min2Cycle。映射fst,我可以做fst。min2Cycle。在高尔夫球代码中,这意味着将f $ map fst替换为fst $ f,从而节省了4个字节。

编辑3:多亏了hadhaskeller,又节省了六个字节!


1
您可以在“半”功能处将div(x + 1 + rem x 2)替换为div(x + 1)2
骄傲的haskeller 2014年

我实际上有自己的解决方案,有49个字符,并且可以解决O(log n),但我只有2个投票;-(。我不明白为什么
骄傲的haskeller 2014年

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}
大黄奶油蛋ust 2014年

4

C 95 97

编辑 Typedef,由@Michaelangelo建议

这或多或少是Heron算法的直接实现。唯一的怪癖是计算避免整数溢出的平均值:a =(m + n)/ 2对biiiig数不起作用。

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;
}

避免溢出的出色工作-不仅要正确执行它,而且还要首先考虑并进行测试。绝对赞赏。
托德·雷曼

顺便说一句,有趣的是在某些CPU上的划分可能是多么昂贵。即使该算法执行的时间大约是算盘算法的一半,但运行时却比我在Core i7 CPU上进行基准测试时算盘算法慢约5倍,这不喜欢做除法。无论如何,但是运行时在这里并不重要-只是大小。:)太好了!!!
托德·雷曼

4

C#64 62 55

由于这是一个(并且我对数学很糟糕),并且运行时只是一个建议,因此我完成了在线性时间内运行的简单方法:

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

在dotnetfiddle上测试

当然,对于较大的输入,它的速度非常慢。


1
通过更改return i-1return--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,单击“蠕变”,然后单击“运行”,它得出了4!:)
Todd Lehman 2014年

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

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字符进行惩罚的不利原因。;)(也就是说,这个相当古老的挑战似乎还是
Martin Ender

@MartinEnder感谢您的热烈欢迎和技巧:)
QuantumKarl 2016年

1
01!使用Dyalog APL,您可以设置⎕DIV←1(许多默认设置)来获取正确的结果。
亚当

2

C99(108个字符)

这是我自己在C99中的解决方案,该解决方案是根据Wikipedia上一篇文章中的算法改编而成的。我相信在其他语言中肯定有可能做得比这更好。

打高尔夫球:

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 —再次感谢您指出这一点。我更新了代码以反映这一点,并添加了其他一些高尔夫技巧。还添加了原始断言,并const在非高尔夫版本中将其设为n 。
托德·雷曼

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 2014年

2

Powershell(52)限于Int32(-2,147,483,648至2,147,483,647)

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

我现在对Powershell尖叫,试图使最后一个测试用例正常工作,但是无论我做什么,Powershell都会使用管道变量$ _作为Int32结束工作,而我现在找不到解决方法。

因此,我现在只限制我的答案。如果我能找到更好的方法来处理uint64s,我将进行编辑。(顺便说一下,最后一个测试用例对于Powershell的普通Int64类型来说太大了!)

这是一些测试用例(我用来跟踪时间的一些额外输出)

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

注意:自2011年以来,R并没有像我想象的那样内置支持64位整数。这些答案在技术上可能是无效的,但是在过去三年中R再次发生了很大变化。


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

C,38

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

我提交的Forth译文。慢但正确。O(√n)在OS X(64位)上测试。


2

dc,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字节

我第一次尝试打高尔夫球。看起来它是C语言中满足“有效”要求的最短的程序,因为它运行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吗?
Todd Lehman

1
@ToddLehman Nope,只是想念那些带出去。好赶上!
克里斯

顺便说一句,我喜欢这种a--+1避免写作的方式a-- != UINT64_C(-1)。您在某个地方学到了这个技巧还是自己发明了?
Todd Lehman

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 2014年

@proud haskeller为什么禁止全局变量?
mantale 2014年

@mantal,因为您必须提供可运行的程序/方法。
Marciano.Andrade

@ Marciano.Andrade给出的代码是可运行的。
故事

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是的,但是是一个函数,但不能像a那样从程序内部调用f(y)。)
Todd Lehman

我认为您可以省略最里面的一对括号并写成while((d=x*x-y)>0.5)代替while((d=(x*x-y))>0.5)。再保存2个字符。:)
Todd Lehman

每0.5可以更改为0.5
Yttsi 2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.