整数の整数平方根[閉じた]


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(√n)時間で答えを繰り返し上方にスキャンできると確信していますが、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 ++を使用している場合、たとえば、uint64_tおよびuint32_tで定義されているように、符号なしの64ビットおよび32ビット整数型が存在すると想定できます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
ジョンドヴォルザーク14

1
2/4はカウントされますか?
ミロ14

1
とにかく、ほとんどの浮動小数点データ型には、このタスクに必要な精度がありません。入力範囲全体に対して53ビットは十分ではありません。
user2357112がサポートするMonica 14

回答:


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はないため(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. ";

ハハハ!おっと、わかりました、あなたは私に技術を教えてくれました。分数の力はないと言っておくべきでした。しかし、あなたのコードは確かに述べられた規則に従っているので、私はそれを支持しています。:)
トッドリーマン14

2
CJamには、入力範囲全体をカバーするために、任意精度の小数がありますか?
isaacg 14

また、intへのキャストでNaN-> 0を使用するハックもあります。
isaacg 14

きちんとしたアイデア、それはJでもまったく同じ文字数で表すことができます:<.@%~^&1.5。これを別の回答として投稿することはできますか(基本的にあなたの正確なポートであるため)。
ɐɔıʇǝɥʇuʎs

@ɐɔıʇǝɥʇuʎs:どうぞ。しかし、最後のテストケースを含め、私のソリューションが大きな数値に対して誤って丸められる可能性があることがわかりました。私の守備では、それが唯一の理由は、私の検査に合格4294967295し、4294967296見て非常に ...似て
デニス

10

ハスケル、28 26

これは、ゴルフ用に設計されていない言語からの最短のエントリーだと思います。

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

sパラメータaを持つ関数に名前を付け、1から最初の数値を引いた値を返しますa。非常にゆっくり実行されます(O(sqrt n)、多分?)。


1
リストインデックス([...]!!0)はheadより短くないでしょうか?
isaacg 14

@isaacgはい、そうです。ありがとう:-)
ザック14

7

Golfscript、17文字

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

好きな方法で関数に名前を付けることができましたが、名前をまったく付けないことにしました。2つの文字を追加して名前を付け、3つを追加して名前を付けてスタックに残さず、完全なプログラムを提供しても問題ない場合は1文字を減算します。

この憎悪は、O(sqrt n)時間ではなく、入力値の対数時間ではなく、結果を生成するために百日足の線形時間を要します。また、それだけのスペースが必要です。絶対に恐ろしい。しかし...これはコードゴルフです。

アルゴリズムは次のとおりです。

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

大好きです!!よくやった!それは美しく曲がりくねっている。
トッドリーマン14

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

Retina(非競合-言語はチャレンジよりも新しい)、43

この答えに取り組んでいる間、網膜を使用して整数の平方根を計算するために同様の方法を使用できることがわかりました:

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

1+

これは、完全な二乗がとして表現できるという事実1+3+5+7+...と、当然ながら、この表現の項の数が平方根であることに依存しています。

オンラインでお試しください。(複数のテストケースを実行できるように最初の行が追加されました。)

明らかに小数から単項への変換のため、これは比較的小さな入力に対してのみ機能します。


4
(言語はチャレンジよりも新しい)
mbomb007

@ mbomb007まあまあ-見出しを編集しました。この答えは間違いなく「できるから」というカテゴリーにあり、意味のある方法で挑戦することを意図したものではありません。
デジタル外傷


6

Perl、133文字

決して最短ではありませんが、桁ごとのアルゴリズムを使用してあらゆるサイズの入力を処理し、O(log n)時間で実行します。文字列としての数字と数字としての数字の間で自由に変換します。可能な最大の製品は、1桁の平方を持つ平方根なので、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文字を削るだろうと思います。
トッドリーマン14

そして、これはビットあまのじゃくを取得しているが、私はあなたが書き換えることにより、よりまだ1をオフに剃ることができると思うforとループを:$a<($z=$_*(20*$r+$_))or$y=$z,$d=$_ for(1..9);
トッド・リーマン

最初の提案は機能しません(という名前のハッシュの長さを取得しようとします%2)が、他の提案は有効です。私はそれらをうまくいく。
ホッブズ

1
@ToddLehman後置記号forは括弧を必要としません。それをあなたの提案に加えると、合計6文字になります。ありがとう!
ホッブズ14

5

Matlab(56)/ Octave(55)

固定小数点法を使用して平方根を計算します。(引数として2 ^ 64-1の場合)最大36ステップで収束し、「可能性のある」整数ルートの下位のものであるかどうかをチェックします。常に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 14

1
基本的にはen.wikipedia.org/wiki/…であり、3700年前と推定されている最も初期の既知の数値手法の1つです。en.wikipedia.org/wiki/Banach_fixed-point_theoremで正当化することができます。これは驚くほど簡単な証明です。本当に素晴らしい=)
flawr 14

5

ルビー— 36文字

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

よくできました!最悪の場合の実行時間は?
トッドリーマン14

g * g <nで、答えがまだ希望の値に近くない場合はどうでしょうか?スクリプトは停止しませんか?
WallyWest 14

1
@ToddLehman私は正直に知りません。:-/これはバビロニアの方法です。ここに、平均的な複雑さの良い証拠と思われるものがあります。数字自体の最初の推測はかなり悪いですが、最悪の場合を理解するには、その証拠をじっと見て真似する必要があります。空き時間ができたら試してみます。:-)
OI 14

@WallyWest私の理解では、whilegは希望値であるfloor(√n)に収束するとループが正確に終了します。これが当てはまらない場合がありますか?
OI

4

パイソン(39)

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

自然な再帰的アプローチ。平方根が高すぎるまで潜在的な平方根をカウントし、1 ずつ減少します。StacklessPythonを使用します。スタックの深さを超えることが心配な場合は、します。

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=00による除算エラーを与えないために、私は多くの文字を費やしています。そうでない場合、18文字を行うことができます

lambda n:n**1.5//n 

ルールでは、関数に名前を付ける必要はありませんでした(「関数に好きな名前を付けることができます。」の解釈方法によって異なります)。


—ありがとう、それを明確にします。関数でなければなりません。名前を付ける必要はありません。したがって、ラムダ関数は問題ありません。私はそれを考えていたら、最初からこれに言及していたでしょう。質問を投稿したとき、Cの観点から考えすぎていました。
トッドリーマン14

4

C99(58文字)

これは私が良いとは思わない答えの例ですが、コードゴルフの観点からは私にとって興味深いものです。

オリジナル:64文字

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

これがひどいのは、O(log(n))時間ではなくO(√n)時間で実行されるためです。(nは入力値です。)

編集:63文字

r-1to --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文字

関数の呼び出しで、たとえばr(n,0)justではなく、2番目のパラメーターを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 14

@Fors —素晴らしいアプローチです!残念ながら、それは1の入力に対してゼロ除算を引き起こしませんか?また、0の入力に対して何をしますか?ので--nときn==0-1になり、これらは符号なしの値でありますので、-12⁶⁴-1になります。
トッドリーマン14

1
#define Z uint64_t ...またはtypedefはカップルを節約します
テクノサウルス14

@technosaurus —ええ、2を節約します。ありがとう。:-)
トッドリーマン14

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文字短いです。よくやった!
トッドリーマン14

これを修正して入力に対して正しい答えを出すには、1おそらく2文字が必要です。
ピーターテイラー14

4

ハスケル、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」の短縮版として「0 <1」に置き換えて2バイトを保存し、g * gをインライン化してさらに2バイトを保存しました。

また、O(sqrt(n))に満足している場合は、単に行うことができます

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

35文字ですが、それは何が楽しいですか?

編集2:私はちょうどペアが辞書順でソートされているので、min2Cycleを実行するのではないことに気付きました。map fst、私はちょうどfstを行うことができます。min2Cycle。ゴルフコードでは、f $ map fstをfst $ fに置き換え、さらに4バイト節約します。

編集3: proudhaskellerのおかげでさらに6バイト節約しました!


1
「div」関数で(div x 2 + rem x 2)をdiv(x + 1)2に置き換えることができます
誇りに思ってhaskeller 14

私は実際にはO(ログn)は49文字、および解きを持っている自分のソリューションを持っているが、私は2 upvotesを持って;-(私はなぜ理解していない。
誇り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>を使用できます。編集を試みましたが、あなたは同時に編集していたので、あなたにそれをさせます。
レインボルト14

1
それとも、このような41文字でそれを行うことができます:function s(n){for(a=1;++a*a<n;);return a}
ルバーブカスタード

4

C 95 97

編集@Michaelangeloによって提案されたTypedefの

これは、多少なりともHeronアルゴリズムの簡単な実装です。唯一の癖は、整数のオーバーフローを回避して平均値を計算することです。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;
}

オーバーフロー回避の素晴らしい作業-それを正しく行うためだけでなく、そもそもそれについて考えてテストするように注意してください。間違いなく感謝します。
トッドリーマン14

ところで、一部のCPUで割高な除算ができることは面白いです。このアルゴリズムは、そろばんアルゴリズムのおよそ半分のステップで実行されますが、Core i7 CPUでベンチマークを実行すると、除算が好きではないため、そろばんアルゴリズムよりも約5倍遅いランタイムになります。とにかく、ここではランタイムは重要ではなく、サイズのみです。:)とてもいい仕事!!!
トッドリーマン14

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
トッドリーマン14

i*i<=aでは、それは通常の型の整数演算であることが保証されていますか?(私はC#に詳しくありません。)その場合、およびC#がCのようにブール値への暗黙的な整数変換を許可する場合、それをに変更することでもう1文字を保存できる場合がありますa/i/i
トッドリーマン14

1
@ToddLehman Decimal乗算結果が潜在的に1ステップ進む可能性があるため、オーバーフローを回避するために、実際には固定小数点演算(、最大値と精度が高い)になりUInt64.MaxValueます。ただし、C#にはブール型への暗黙的な変換はありません。でも変更できるはずreturnです、ありがとう。コンピューターに戻ったらそれをします。
ボブ14

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
Ok, that is just cool. Nice work! I entered 17, clicked Creep and then Run, and it came up with 4! :)
Todd Lehman

3

Cobra - 62

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

Batch - 74

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


3

J (10)

Very, very, very inspired by the answer of @Dennis:

<.@%~^&1.5

And a slightly longer, but with better performance (I suspect):

<.@(-:&.^.)

floor(halve under log)

To execute, indented parts are input:

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

How do you execute this for a given integer?
Dennis

1
@Dennis See answer
ɐɔıʇǝɥʇuʎs

3

APL - 12 chars, 19 bytes

{⌊(⍵*1.5)÷⍵}

example use:

{⌊(⍵*1.5)÷⍵}17

returns 4

Test vector

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

returns

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

Try Online

Big thanks to: user "ssdecontrol" for algorithm


1
Welcome to PPCG! We normally score APL as one byte per character. Unless the challenge specifies it, there is no need to count in UTF-8. Any existing encoding is fine, and there is an old APL codepage from back in the day which uses a single byte for each character. The fact that APL predates ASCII is a bad reason to penalise it for using non-ASCII characters. ;) (That said, this rather old challenge seems to score by characters anyway.)
Martin Ender

@MartinEnder Thanks for the warm welcome and tips :)
QuantumKarl

1
01! Using Dyalog APL, you can set ⎕DIV←1 (which many use as default) to obtain the correct result.
Adám

2

C99 (108 characters)

Here is my own solution in C99, which is adapted from an algorithm in an article on Wikipedia. I'm sure it must be possible to do much better than this in other languages.

Golfed:

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

Partially golfed:

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

Ungolfed:

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
Suggestion: No need for a, use n.
edc65

Ah yes. Thank you. In my original version, I was maintaining n so that just before returning I could make the assertion (not shown) that r^2 <= n < (r+1)^2. With that assertion omitted, it's longer necessary to keep n intact.
Todd Lehman

@edc65 — Thanks again for pointing that out. I updated my code to reflect that, as well as added a couple other golf tricks. Also added the original assertions and made n const in the ungolfed version.
Todd Lehman

2

JavaScript 73 81 (to comply with 64-bit numbers requirement)

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

Implementing Heron of Alexandria's algorithm...


Nice! Does this work for all unsigned 64-bit integer inputs?
Todd Lehman

Try as I might this only seems to work up to 32-bit... Much to my disappointment...
WallyWest

Surely the last |0 truncates any value to 32 bit. Using Math.floor instead?
edc65

@edc65 You're right actually, seems |0 affects up to 32-bit whereas Math.floor is more effective at 64-bit... I've updated my code, having to take an extra 8 characters in order to do so...
WallyWest

@edc65 I've just had a thought... would ~~x work in 64-bit?
WallyWest

2

Powershell (52) Limited to Int32 (-2,147,483,648 to 2,147,483,647)

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

I'm screaming at Powershell right now trying to make the last test case work but no matter what I do Powershell winds up using the pipeline variable $_ as an Int32, and I can't find a way around it right now.

So I'll just limit my answer for now. If I can find a better way to handle uint64s I will edit. (The last test case is too big for Powershell's normal Int64 type, by the way!)

Here are a few test cases (with a bit of extra output I used to track the time)

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

I don't know my O()s, but this seems like a pretty dramatic jump.


2

Caveat: as of 2011, R had no built-in support for 64 bit integers as I had assumed it did. These answers might be invalid on that technicality, but then again R has changed a lot in the last 3 years.


R, 85

Using Newton's method:

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

which converges quadratically. +2 characters to assign the function to a variable for benchmarking:

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

Brute force:

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

And the same check:

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

The cheap/brilliant exponentiation trick:

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

which also happens to be very fast (although not as fast as the built-in):

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

Translation of my Forth submission. Slow but correct. O(√n). Tested on OS X (64 bit).


2

dc, 50 bytes

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

Spaced out and explained:

               # 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

Uh, looks like the last test case crashes. I'll try to fix it.
Joe

Resolved. Now accepts very large input; serendipitously, the fix allowed me to remove some ugly code at the beginning.
Joe

2

C, 139 137 136 bytes

My first try at code golf. It looks like it's the shortest in C that fits the "efficient" requirement, as it runs in O(log n) time, using only addition and bit shifts. Though I'm sure it could be shorter yet...

It should work just fine for larger integer values too as long as the a=32 part is changed to 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;}

Nice work! I haven't run it to test, but the code looks interesting. Is there a reason you wrote (t++) instead of just t++ in the assignment to r?
Todd Lehman

1
@ToddLehman Nope, just missed taking those out. Nice catch!
Chris

BTW, I love the a--+1 as a way to avoid writing a-- != UINT64_C(-1). Did you learn that trick somewhere or invent it yourself?
Todd Lehman

1
@ToddLehman Thanks! I figured that out myself.
Chris

1

C - 50 (61 without global)

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

It use global variables as parameter and return value to save space.

No global version :

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

1
I don't think using global variables is legal. At least tell how long it would be legitimately and provide a legitimate version
proud haskeller

@proud haskeller Why would global variables be forbidden ?
mantale

@mantal because you must provide a runnable program/method.
Marciano.Andrade

@Marciano.Andrade the code is gave is runnable.
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;
}

Nice! How about x+=(d<0)-0.5; ... saves 5 more characters?
Todd Lehman

BTW, this isn't (but should be) in the form of a function, as mentioned in the problem statement. (Okay, technically, yeah, main is a function, but it's not callable from inside a program like an f(y) would be.)
Todd Lehman

I think you can omit the innermost pair of parentheses and write while((d=x*x-y)>0.5) instead of while((d=(x*x-y))>0.5). Saves 2 more characters. :)
Todd Lehman

Every 0.5 can be changed to .5
Yytsi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.