一緒に4つの正方形


19

ラグランジュの4平方定理は、任意の自然数が4平方数の合計として表現できることを示しています。あなたの仕事はこれを行うプログラムを書くことです。

入力:自然数(10億未満)

出力:正方形の合計がその数になる4つの数値(順序は関係ありません)

注:ブルートフォース検索を行う必要はありません!詳細はこちらこちら。この問題を単純化する関数がある場合(決定します)、それは許可されません。自動化された素数関数と平方根が許可されています。複数の表現がある場合、どれでも構いません。総当たり攻撃を行うことを選択した場合は、妥当な時間(3分)以内に実行する必要があります

サンプル入力

123456789

サンプル出力(どちらでも構いません)

10601 3328 2 0
10601 3328 2

コードを短くする場合、ブルートフォースを実行できますか?
マーティンエンダー

@ m.buettnerはい、それは大きな数字を扱う必要があります
QWR

@ m.buettner投稿を読んでください。10億未満の自然数
qwr

ああ、見落としていました。
マーティンエンダー

2
@Dennis自然数は、この場合には0に含まれていません
QWR

回答:


1

CJam、50バイト

li:NmF0=~2/#:J(_{;)__*N\-[{_mqJ/iJ*__*@\-}3*])}g+p

私の3番目の(そして最後の約束です)答え。このアプローチは、primoの答えに大きく基づいています

CJamインタープリターでオンラインで試してください。

使用法

$ cjam 4squares.cjam <<< 999999999
[189 31617 567 90]

バックグラウンド

  1. primoの更新されたアルゴリズムを見た後、CJam実装がどのように得点するかを見なければなりませんでした。

    li{W):W;:N4md!}g;Nmqi)_{;(__*N\-[{_mqi__*@\-}3*])}g+2W#f*p
    

    わずか58バイト!このアルゴリズムはほぼ一定の時間で実行され、の異なる値に対してあまり変化を示しませんN。それを変えましょう...

  2. で開始しfloor(sqrt(N))て減少する代わりに、開始し1て増加することができます。これにより、4バイト節約されます。

    li{W):W;:N4md!}g;0_{;)__*N\-[{_mqi__*@\-}3*])}g+2W#f*p
    
  3. として表現する代わりに、–が最小の素因数である– Nとして表現して4**a * b、さらに1バイト節約できます。p**(2a) * bpN

    li_mF0=~2/#:J_*/:N!_{;)__*N\-[{_mqi__*@\-}3*])}g+Jf*p
    
  4. 前の変更により、実装をわずかに変更できます(アルゴリズム自体に触れることなく):で除算Np**(2a)て乗算する代わりにp**a、可能な解をの倍数に直接制限できますp**a。これにより、さらに2バイト節約できます。

    li:NmF0=~2/#:J!_{;J+__*N\-[{_mqJ/iJ*__*@\-}3*])}g+`
    
  5. 最初の整数を倍数に制限しないとp**a、追加のバイトが節約されます。

    li:NmF0=~2/#:J(_{;)__*N\-[{_mqJ/iJ*__*@\-}3*])}g+`
    

最終的なアルゴリズム

  1. 検索ab、そのようなことはN = p**(2a) * b、どこbの倍数でないp**2pの最小の素因数ですN

  2. 設定しj = p**aます。

  3. 設定しk = floor(sqrt(N - j**2) / A) * Aます。

  4. 設定しl = floor(sqrt(N - j**2 - k**2) / A) * Aます。

  5. 設定しm = floor(sqrt(N - j**2 - k**2 - l**2) / A) * Aます。

  6. の場合N - j**2 - k**2 - l**2 - m**2 > 0、設定j = j + 1して手順3に戻ります。

これは次のように実装できます。

li:N          " Read an integer from STDIN and save it in “N”.                        ";
mF            " Push the factorization of “N”. Result: [ [ p1 a1 ] ... [ pn an ] ]    ";
0=~           " Push “p1” and “a1”. “p1” is the smallest prime divisor of “N”.        ";
2/#:J         " Compute p1**(a1/2) and save the result “J”.                           ";
(_            " Undo the first two instructions of the loop.                          ";
{             "                                                                       ";
  ;)_         " Pop and discard. Increment “J” and duplicate.                         ";
  _*N\-       " Compute N - J**2.                                                     ";
  [{          "                                                                       ";
    _mqJ/iJ*  " Compute K = floor(sqrt(N - J**2)/J)*J.                                ";
    __*@      " Duplicate, square and rotate. Result: K   K**2   N - J**2             ";
    \-        " Swap and subtract. Result: K   N - J**2 - K**2                        ";
  }3*]        " Do the above three times and collect in an array.                     ";
  )           " Pop the array. Result: N - J**2 - K**2 - L**2 - M**2                  ";
}g            " If the result is zero, break the loop.                                ";
+p            " Unshift “J” in [ K L M ] and print a string representation.           ";

ベンチマーク

Intel Core i7-3770で最大999,999,999までのすべての正の整数で5つのバージョンすべてを実行し、実行時間を測定し、解決策を見つけるために必要な反復をカウントしました。

次の表は、1つの整数の平均反復回数と実行時間を示しています。

Version               |    1    |    2    |    3    |    4    |    5
----------------------+---------+---------+---------+---------+---------
Number of iterations  |  4.005  |  28.31  |  27.25  |  27.25  |  41.80
Execution time [µs]   |  6.586  |  39.69  |  55.10  |  63.99  |  88.81
  1. わずか4回の繰り返しと整数あたり6.6 マイクロ秒で、primoのアルゴリズムは非常に高速です。

  2. 始まるfloor(sqrt(N))残りの3乗の和のために小さい値でこの葉たちをするので、より多くの意味になります。予想どおり、1から開始するのはかなり遅くなります。

  3. これは、うまく実装されていない良いアイデアの古典的な例です。実際にコードサイズを小さくするにmF、整数を因数分解するに依存しNます。バージョン3はバージョン2よりも少ない反復回数で済みますが、実際にはかなり遅くなります。

  4. アルゴリズムは変更されませんが、バージョン4は非常に低速です。これは、各反復で追加の浮動小数点除算と整数乗算を実行するためです。

  5. inputに対してN = p**(2a) ** b、アルゴリズム5には(k - 1) * p**a + 1反復が必要になります。ここkで、アルゴリズム4に必要な反復回数です。k = 1またはの場合a = 0、違いはありません。

    ただし、フォームの入力は4**a * (4**c * (8 * d + 7) + 1)パフォーマンスが非常に悪い場合があります。開始値j = p**aN - 4**a = 4**(a + c) * (8 * d + 7)ので、3つの平方の合計として表すことはできません。したがって、k > 1少なくともp**a反復が必要です。

    ありがたいことに、primoの元のアルゴリズムは信じられないほど高速であり、であるN < 1,000,000,000。手作業で見つけることができる最悪のケースは265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1)、6,145回の反復が必要です。私のマシンでの実行時間は300ミリ秒未満です。平均して、このバージョンはprimoのアルゴリズムの実装よりも13.5倍遅いです。


Nとして表現する代わりに、4**a * bそれをとして表現できp**(2a) * bます。」これは実際には改善です。これを含めたかったのですが、もっと長くなりました(理想は最大の完全な二乗係数を見つけることです)。「1から始めて増分することで4バイト節約できます。」これは間違いなく遅いです。特定の範囲の実行時間は4〜5倍です。「999,999,999までのすべての正の整数は24.67時間かかり、整数あたりの平均実行時間は0.0888ミリ秒になりました。」Perlは全範囲を処理するのにたった2.5時間しかかからず、Pythonの翻訳は10倍高速になりました;)
primo

@primo:はい、そうです。分割p**aは改善ですが、小さなものです。最大の2乗係数で除算すると、1から開始すると大きな違いが生じます。平方根の整数部分から開始する場合は、依然として改善されています。それを実装すると、あと2バイトしかかかりません。ひどい実行時間は、CJamではなく、私の改善のせいではないようです。すべてのアルゴリズム(提案したものを含む)のテストを再実行し、壁時間を測定するのではなく反復をカウントします。のは、どのくらい見てみましょうことが ...取る
デニス

最大の二乗係数を見つけるのに必要なバイト数は2バイトだけです!?これはどんな魔術ですか?
プリモ

@primo:整数がスタック上にある場合、1\それを1(アキュムレータ)と交換し、mFその因数分解をプッシュし、{~2/#*}/すべての素因数を指数で2で割った値に上げてから、アキュムレータで乗算します。アルゴリズムを直接実装するには、2バイトしか追加しません。CJamを持っている(ように見える)がないので、小さな違いは、主に私は4の指数を見つけなければならなかった厄介な方法が原因でしばらく ...ループ
デニス

とにかく、ベンチマークは終了しました。最大2乗係数を見つけることなく1,000,000個すべての整数を因数分解するために必要な反復の合計数は4,004,829,417で、実行時間は1.83時間です。最大二乗係数で除算すると、反復回数は3,996,724,799に減少しますが、時間は6.7時間に増加します。因数分解のように見えるが...正方形を見つけることよりも多くの時間がかかる
デニス

7

FRACTRAN:156 98分数

これは古典的な数論の問題であるため、これを解決するのに数字を使用するよりも良い方法があります!

37789/221 905293/11063 1961/533 2279/481 57293/16211 2279/611 53/559 1961/403 53/299 13/53 1/13 6557/262727 6059/284321 67/4307 67/4661 6059/3599 59/83 1/59 14279/871933 131/9701 102037079/8633 14017/673819 7729/10057 128886839/8989 13493/757301 7729/11303 89/131 1/89 31133/2603 542249/19043 2483/22879 561731/20413 2483/23701 581213/20687 2483/24523 587707/21509 2483/24797 137/191 1/137 6215941/579 6730777/965 7232447/1351 7947497/2123 193/227 31373/193 23533/37327 5401639/458 229/233 21449/229 55973/24823 55973/25787 6705901/52961 7145447/55973 251/269 24119/251 72217/27913 283/73903 281/283 293/281 293/28997 293/271 9320827/58307 9831643/75301 293/313 28213/293 103459/32651 347/104807 347/88631 337/347 349/337 349/33919 349/317 12566447/68753 13307053/107143 349/367 33197/349 135199/38419 389/137497 389/119113 389/100729 383/389 397/383 397/39911 397/373 1203/140141 2005/142523 2807/123467 4411/104411 802/94883 397/401 193/397 1227/47477 2045/47959 2863/50851 4499/53743 241/409 1/241 1/239

2 n ×193 という形式の入力を受け取り、3 a ×5 b ×7 c ×11 dを出力します。あなたが持っている場合は3分で実行される可能性は本当に良い通訳を。多分。

...大丈夫、そうでもない。これは私がいることをFRACTRANに行うには、このような楽しい問題であるように見えていたことをしようとします。明らかに、これは時間の要件を満たしていないため(ブルートフォース)、ゴルフもほとんど行われていないため、質問に対する適切な解決策ではありませんが、Codegolfの質問は毎日ではないため、ここに投稿すると思いましたFRACTRANで行うことができます;)

ヒント

このコードは、次の擬似Pythonと同等です。

a, b, c, d = 0, 0, 0, 0

def square(n):
    # Returns n**2

def compare(a, b):
    # Returns (0, 0) if a==b, (1, 0) if a<b, (0, 1) if a>b

def foursquare(a, b, c, d):
    # Returns square(a) + square(b) + square(c) + square(d)

while compare(foursquare(a, b, c, d), n) != (0, 0):
    d += 1

    if compare(c, d) == (1, 0):
        c += 1
        d = 0

    if compare(b, c) == (1, 0):
        b += 1
        c = 0
        d = 0

    if compare(a, b) == (1, 0):
        a += 1
        b = 0
        c = 0
        d = 0

7

Mathematica 61 66 51

3つの方法が示されています。最初のアプローチのみが時間の要件を満たしています。


1-FindInstance(51文字)

これにより、方程式の単一のソリューションが返されます。

FindInstance[a^2 + b^2 + c^2 + d^2 == #, {a, b, c, d}, Integers] &

例とタイミング

FindInstance[a^2 + b^2 + c^2 + d^2 == 123456789, {a, b, c, d}, Integers] // AbsoluteTiming

{0.003584、{{a-> 2600、b-> 378、c-> 10468、d-> 2641}}}

FindInstance[a^2 + b^2 + c^2 + d^2 == #, {a, b, c, d}, Integers] &[805306368]

{0.004437、{{a-> 16384、b-> 16384、c-> 16384、d-> 0}}}


2-IntegerPartitions

これも機能しますが、速度要件を満たすには遅すぎます。

f@n_ := Sqrt@IntegerPartitions[n, {4}, Range[0, Floor@Sqrt@n]^2, 1][[1]]

Range[0, Floor@Sqrt@n]^2は、平方根n(パーティション内の可能な最大の正方形)より小さいすべての正方形のセットです。

{4}n上記の正方形のセットの4つの要素で構成される整数パーティションが必要です。

1、関数内でIntegerPartitions最初の解を返します。

[[1]]外側の中括弧を削除します。ソリューションは1つの要素のセットとして返されました。


f[123456]

{348、44、20、4}


3-PowerRepresentations

PowerRepresentationsは、4乗問題のすべての解を返します。また、他の力の合計を解くことができます。

PowersRepresentationsは、123456789を4つの正方形の合計として表現する181の方法を5秒未満で返します。

n= 123456;
PowersRepresentations[n, 4, 2] //AbsoluteTiming

ゾル

ただし、他の金額には遅すぎます。


Mathematicaは、総当たり攻撃を高速に実行します。IntegerPartitionsは、セットのDFT畳み込みのように、あらゆる組み合わせを試すよりもはるかに賢いことをしていますか?仕様では、ちなみに、正方形ではなく数字を求めています。
xnor

Mathematicaはブルートフォースを使用していると思いますが、おそらく最適化されていIntegerPartitionsます。タイミングからわかるように、速度は、最初の(最大の)数値がの平方根に近いかどうかによって大きく異なりますn。以前のバージョンで仕様違反をキャッチしていただきありがとうございます。
DavidC

ベンチマークしてもらえますf[805306368]か?最初に4の累乗で割らずに、私のソリューションは999999999で0.05秒かかります。私は... 805306368 5分後のためのベンチマークを中止されました
デニス

f[805306368]{16384, 16384, 16384}21分後に戻ります。{4}の代わりに{3}を使用しました。4つの非ゼロの正方形の合計でそれを解決しようとする試みは、数時間の実行後に失敗しました。
DavidC

私はMathematicaにアクセスできませんが、ドキュメントセンターで読んだものからIntegerPartitions[n,4,Range[Floor@Sqrt@n]^2も同様に動作するはずです。ただし、質問で指定された時間制限に準拠していないため、スコアに方法1を使用する必要はないと思います。
デニス

7

Perl- 116バイト 87バイト(以下の更新を参照)

#!perl -p
$.<<=1,$_>>=2until$_&3;
{$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$.}($j++)x4;$n&&redo}
$_="@a"

シバングを1バイトとして数え、水平方向の健全性のために改行を追加しました。

組み合わせが送信。

平均的な(最悪?)ケースの複雑さはO(log n) O(n 0.07)のようです。実行速度が0.001秒より遅いことはありませんでした。900000000〜999999999の範囲全体をチェックしました。それよりもかなり長い時間(0.1秒以上)を要するものを見つけた場合は、お知らせください。


サンプルの使用法

$ echo 123456789 | timeit perl four-squares.pl
11110 157 6 2

Elapsed Time:     0:00:00.000

$ echo 1879048192 | timeit perl four-squares.pl
32768 16384 16384 16384

Elapsed Time:     0:00:00.000

$ echo 999950883 | timeit perl four-squares.pl
31621 251 15 4

Elapsed Time:     0:00:00.000

これらの最後の2つは、他の提出物の最悪のシーンのようです。どちらの場合でも、示されている解決策は、文字通り非常に最初にチェックされるものです。の場合123456789、2番目です。

値の範囲をテストする場合は、次のスクリプトを使用できます。

use Time::HiRes qw(time);

$t0 = time();

# enter a range, or comma separated list here
for (1..1000000) {
  $t1 = time();
  $initial = $_;
  $j = 0; $i = 1;
  $i<<=1,$_>>=2until$_&3;
  {$n=$_;@a=map{$n-=$a*($a-=$_%($b=1|($a=0|sqrt$n)>>1));$_/=$b;$a*$i}($j++)x4;$n&&redo}
  printf("%d: @a, %f\n", $initial, time()-$t1)
}
printf('total time: %f', time()-$t0);

ファイルにパイプするときに最適です。1..1000000私のコンピューターでは、範囲は約14 999000000..1000000000秒(1秒あたり71000値)で、範囲は約20秒(1秒あたり50000値)で、O(log n)平均複雑度と一致しています


更新

編集:このアルゴリズムは、少なくとも1世紀の間、電卓で使用されてきたものと非常に似ていることがわかりました

最初に投稿してから、1..1000000000の範囲のすべての値をチェックしました。「最悪のケース」の動作は値699731569で示され、ソリューションに到達する前に合計190の組み合わせをテストしました。190を小さな定数であると見なす場合-そして私は確かにそうする-必要な範囲での最悪の場合の動作はO(1)と見なすことができます。つまり、巨大なテーブルからソリューションを検索するのと同じくらい高速で、平均してかなり高速です。

もう一つ。190回の反復後、144400を超えるものはすべて、最初のパスを超えていません。幅優先のトラバースのロジックは価値がありません-使用されていません。上記のコードはかなり短くすることができます:

#!perl -p
$.*=2,$_/=4until$_&3;
@a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;
$_="@a"

これは、検索の最初のパスのみを実行します。ただし、2番目のパスを必要とする144400未満の値がないことを確認する必要があります。

for (1..144400) {
  $initial = $_;

  # reset defaults
  $.=1;$j=undef;$==60;

  $.*=2,$_/=4until$_&3;
  @a=map{$=-=$%*($%=$=**.5-$_);$%*$.}$j++,(0)x3while$=&&=$_;

  # make sure the answer is correct
  $t=0; $t+=$_*$_ for @a;
  $t == $initial or die("answer for $initial invalid: @a");
}

要するに、範囲1..1000000000には、ほぼ一定の時間解が存在し、あなたはそれを見ています。


更新された更新

@Dennisと私は、このアルゴリズムをいくつか改善しました。興味があれば、以下のコメントの進捗状況とその後の議論をフォローできます。必要な範囲の平均反復回数は4をわずかに超えて1.229に減少し、1..1000000000のすべての値をテストするのに必要な時間は18分 54秒から2分41秒に短縮されました。以前は最悪の場合、190回の反復が必要でした。現在、最悪のケースである85438277821のみ必要です。

最終的なPythonコードは次のとおりです。

from math import sqrt

# the following two tables can, and should be pre-computed

qqr_144 = set([  0,   1,   2,   4,   5,   8,   9,  10,  13,
                16,  17,  18,  20,  25,  26,  29,  32,  34,
                36,  37,  40,  41,  45,  49,  50,  52,  53,
                56,  58,  61,  64,  65,  68,  72,  73,  74,
                77,  80,  81,  82,  85,  88,  89,  90,  97,
                98, 100, 101, 104, 106, 109, 112, 113, 116,
               117, 121, 122, 125, 128, 130, 133, 136, 137])

# 10kb, should fit entirely in L1 cache
Db = []
for r in range(72):
  S = bytearray(144)
  for n in range(144):
    c = r

    while True:
      v = n - c * c
      if v%144 in qqr_144: break
      if r - c >= 12: c = r; break
      c -= 1

    S[n] = r - c
  Db.append(S)

qr_720 = set([  0,   1,   4,   9,  16,  25,  36,  49,  64,  81, 100, 121,
              144, 145, 160, 169, 180, 196, 225, 241, 244, 256, 265, 289,
              304, 324, 340, 361, 369, 385, 400, 409, 436, 441, 481, 484,
              496, 505, 529, 544, 576, 580, 585, 601, 625, 640, 649, 676])

# 253kb, just barely fits in L2 of most modern processors
Dc = []
for r in range(360):
  S = bytearray(720)
  for n in range(720):
    c = r

    while True:
      v = n - c * c
      if v%720 in qr_720: break
      if r - c >= 48: c = r; break
      c -= 1

    S[n] = r - c
  Dc.append(S)

def four_squares(n):
  k = 1
  while not n&3:
    n >>= 2; k <<= 1

  odd = n&1
  n <<= odd

  a = int(sqrt(n))
  n -= a * a
  while True:
    b = int(sqrt(n))
    b -= Db[b%72][n%144]
    v = n - b * b
    c = int(sqrt(v))
    c -= Dc[c%360][v%720]
    if c >= 0:
      v -= c * c
      d = int(sqrt(v))

      if v == d * d: break

    n += (a<<1) - 1
    a -= 1

  if odd:
    if (a^b)&1:
      if (a^c)&1:
        b, c, d = d, b, c
      else:
        b, c = c, b

    a, b, c, d = (a+b)>>1, (a-b)>>1, (c+d)>>1, (c-d)>>1

  a *= k; b *= k; c *= k; d *= k

  return a, b, c, d

これは、2つの事前計算された補正テーブルを使用します。1つはサイズが10kb、もう1つは253kbです。上記のコードにはこれらのテーブルのジェネレーター関数が含まれていますが、これらはおそらくコンパイル時に計算される必要があります。

より控えめなサイズの補正テーブルを持つバージョンは、ここで見つけることができます:http://codepad.org/1ebJC2OVこのバージョンは、平均必要と1.620の最悪のケースでは、用語ごとに反復を38、約3メートル21Sで全範囲を実行。モジュロではなくビット単位andb補正を使用することで、少しの時間を補います。


改善点

偶数の値は、奇数の値よりも解を生成する可能性が高くなります。
以前にリンクされた暗算の記事では、4のすべての要因を削除した後、分解される値が偶数である場合、この値を2で割ることができ、解が再構築されることに注意してください。

これはメンタル計算には意​​味がありますが(値が小さいほど計算が容易になる傾向があります)、アルゴリズム的にはあまり意味がありません。256ランダム4を取る場合タプルを取り、8を法とする平方和を調べた、あなたは値がわかります135、および7は、それぞれの平均に達している32回。ただし、値26はそれぞれ48回に達します。奇数の値に2を掛けると、平均で33%少ない反復で解決策が見つかります。再構成は次のとおりです。

abが同じパリティを持ち、cdも同じになるように注意する必要がありますが、解決策が見つかった場合は、適切な順序が存在することが保証されます。

不可能なパスをチェックする必要はありません。
2番目の値bを選択した後、与えられたモジュロの2次剰余を考えれば、解が存在することはすでに不可能かもしれません。とにかくチェックしたり、次の反復に移動したりする代わりに、bの値は、解につながる可能性のある最小量だけデクリメントすることで「修正」できます。2つの補正テーブルは、これらの値を保存します。1つはbで、もう1つはcです。より高いモジュロを使用すると(より正確には、2次剰余が比較的少ないモジュロを使用すると)、より良い改善が得られます。値aは修正する必要はありません。nを偶数に変更することにより、aは有効です。


1
これが信じられない!...最終的アルゴリズムは、おそらくすべての答えの最も単純で、まだ190回の反復は、それが取るすべてです
デニス

@Dennis他で言及されていなければ、私は非常に驚いたでしょう。見過ごされているのは簡単すぎるようです。
プリモ

1.好奇心が強い:複雑さ分析のテスト値のいずれかが幅優先走査を必要としましたか?2.リンクしたウィキペディアの記事は少しわかりにくいです。Rabin-Shallitアルゴリズムに言及していますが、完全に異なるアルゴリズムの例を提供します。3. Rabin-Shallitアルゴリズムが正確にいつあなたのパフォーマンスを上回るかを見るのは興味深いでしょう。実際には、素数性テストはかなり費用がかかると思います。
デニス

1. 1つではありません。2.ここで情報を取得しました(つまり、このアルゴリズムが存在します)。私は分析を見ていませんし、論文を読んでいません。3.曲線は1e60付近で非常に急勾配になるため、O(log²n)がどの程度「遅い」かは問題ではなく、そのポイント付近で交差します。
プリモ

1
質問の2番目のリンクでは、Rabin-Shallitの実装方法について説明していますが、複雑さについては触れていません。MathOverflowに関するこの回答は、論文の素晴らしい要約を提供します。ところで、1911年にGottfried Ruckleが使用したアルゴリズムを再発見しました(link)。
デニス

6

Python 3(177)

N=int(input())
k=1
while N%4<1:N//=4;k*=2
n=int(N**.5)
R=range(int(2*n**.5)+1)
print([(a*k,b*k,c*k,d*k)for d in R for c in R for b in R for a in[n,n-1]if a*a+b*b+c*c+d*d==N][0])

入力Nを4で割り切れないように減らした後、4つの正方形の合計として表現できる必要があります。そのうちの1つは可能な最大値a=int(N**0.5)またはそれより小さい値で、他の3つの正方形の合計に小さい余りのみを残します。の世話をするために。これにより、検索スペースが大幅に削減されます。

以下に、このコードが常に解決策を見つける証拠を示します。私たちは見つけたいaので、それn-a^2は3つの正方形の合計です。ルジャンドルのスリースクエア定理それがフォームでない限り、数は3乗の和です4^j(8*k+7)。特に、そのような数値は0または3(モジュロ4)です。

連続した2つの値に対してa、2つの連続が残余量N-a^2をそのような形状にすることはできないことを示します。aNことを指摘し、4を法N%4!=0、我々はのうち4つのすべての権限を抽出したのでN

  a%4= 0123
      +----
     1|1010
N%4= 2|2121  <- (N-a*a)%4
     3|3232

2つの連続したagiveがないため(N-a*a)%4 in [0,3]、そのうちの1つは安全に使用できます。そこで、我々は貪欲に可能な限り最大を使用しnn^2<=N、とn-1。以来N<(n+1)^2、残りN-a^2 3つの正方形の合計として表されるは最大で(n+1)^2 -(n-1)^24*n。ですから、までの値のみをチェックするだけで十分2*sqrt(n)ですR

最後の値を反復処理するのではなく、単一のソリューションの後に停止することにより、実行時間をさらに最適化できます d間でのみ検索ますb<=c<=d。しかし、これらの最適化がなくても、最悪のインスタンスは私のマシンで45秒で終了しました。

「for x in R」のチェーンは残念です。おそらく、(a、b、c、d)をエンコードする単一のインデックスを反復処理することにより、文字列の置換または置換によって短縮できます。itertoolsのインポートは価値がないことが判明しました。

編集:引数をよりきれいに、同じ文字数にするためにint(2*n**.5)+1from 2*int(n**.5)+2に変更されました。


これ...私のために仕事をしません5 => (2, 1, 0, 0)
ハリー・ビードル

奇妙なことに、それは私のために機能します:5 => (2, 1, 0, 0)Ideone 3.2.3またはIdle 3.2.2で実行します。あなたは何を得ますか?
xnor

1
@xnor BritishColourはを取得し5 => (2, 1, 0, 0)ます。あなたもコメントを読みましたか?(このコードスニペットを含む3行のコメントがあります。連勝を続けられますか?)
ジャスティン

@Quincunx解読する場合5 => (2, 1, 0, 0)、それはを意味し2^2 + 1^2 + 0^2 + 0^2 = 5ます。それで、はい、できます。
レブム博士14年

1
Quincunx、@ BritishColourのコメントを読んで、私が見る限りで5 => (2, 1, 0, 0)は正しいです。質問の例では、0 ^ 2 = 0が有効な二乗数であると考えています。したがって、私は(xnorがしたように)British Colorが何か他のものを得たと解釈しました。あなたが再び反応しなかったので、英国の色は、あなたが実際に得ると仮定することができ2,1,0,0ますか?
レベルリバーセント

5

CJam91 90 74 71バイト

q~{W):W;:N4md!}gmqi257:B_**_{;)_[Bmd\Bmd]_N\{_*-}/mq_i@+\1%}g{2W#*}%`\;

コンパクトですが、他のアプローチよりも低速です。

オンラインでお試しください!コードを貼り付け、入力に目的の整数を入力し実行をクリックします

バックグラウンド

この投稿は、99バイトのGolfScript回答として始まりました。まだ改善の余地がありましたが、GolfScriptには組み込みのsqrt関数がありません。リビジョン5までGolfScriptバージョンを保持しましたはCJamバージョンと非常に似ていたため、。

ただし、リビジョン6以降の最適化には、GolfScriptで使用できない演算子が必要なため、両方の言語の個別の説明を投稿する代わりに、競争力の低い(およびはるかに遅い)バージョンを削除することにしました。

実装されたアルゴリズムは、総当たりで数値を計算します。

  1. 入力mについてはNWなどを見つけますm = 4**W * N

  2. 設定しi = 257**2 * floor(sqrt(N/4))ます。

  3. 設定しi = i + 1ます。

  4. 整数検索j, k, lようにi = 257**2 * j + 257 * k + l、どこにk, l < 257

  5. d = N - j**2 - k**2 - l**2が完全な正方形かどうかを確認します。

  6. そうでない場合は、手順3に戻ります。

  7. 印刷2**W * j, 2**W * k, 2**W * l, 2**W * sqrt(m)

$ TIME='\n%e s' time cjam lagrange.cjam <<< 999999999
[27385 103 15813 14]
0.46 s
$ TIME='\n%e s' time cjam lagrange.cjam <<< 805306368
[16384 16384 0 16384]
0.23 s

タイミングは、Intel Core i7-4700MQに対応しています。

使い方

q~              " Read and interpret the input. ";
{
  W):W;         " Increment “W” (initially -1). ";
  :N            " Save the integer on the stack in “N”. ';
  4md!          " Push N / 4 and !(N % 4). ";
}g              " If N / 4 is an integer, repeat the loop.
mqi             " Compute floor(sqrt(N/4)). ";
257:B_**        " Compute i = floor(sqrt(N)) * 257**2. ";
_               " Duplicate “i” (dummy value). ";
{               " ";
  ;)_           " Pop and discard. Increment “i”. ";
  [Bmd\Bmd]     " Push [ j k l ], where i = 257**2 * j + 257 * k + l and k, l < 257. ";
  _N\           " Push “N” and swap it with a copy of [ j k l ]. ";
  {_*-}/        " Compute m = N - j**2 - k**2 - l**2. ";
  mq            " Compute sqrt(m). ";
  _i            " Duplicate sqrt(m) and compute floor(sqrt(m)). ";
  @+\           " Form [ j k l floor(sqrt(m)) ] and swap it with sqrt(m). ";
  1%            " Check if sqrt(m) is an integer. ";
}g              " If it is, we have a solution; break the loop. ";
{2W#*}%         " Push 2**W * [ j k l sqrt(m) ]. ";
`\;             " Convert the array into a string and discard “i”. ";

2

C、228

これは、Wikipediaページのアルゴリズムに基づいています。これはO(n)ブルートフォースです。

n,*l,x,y,i;main(){scanf("%d",&n);l=calloc(n+1,8);
for(x=0;2*x*x<=n;x++)for(y=x;(i=x*x+y*y)<=n;y++)l[2*i]=x,l[2*i+1]=y;
for(x=0,y=n;;x++,y--)if(!x|l[2*x+1]&&l[2*y+1]){
printf("%d %d %d %d\n",l[2*x],l[2*x+1],l[2*y],l[2*y+1]);break;}}

2

GolfScript、133 130 129バイト

~{.[4*{4/..4%1$!|!}do])\}:r~,(2\?:f;{{..*}:^~4-1??n*,}:v~)..
{;;(.^3$\-r;)8%!}do-1...{;;;)..252/@252%^@^@+4$\-v^@-}do 5$]{f*}%-4>`

高速ですが、時間がかかります。改行は削除できます。

オンラインでお試しください。オンライン通訳には5秒の時間制限があるため、すべての番号で機能するとは限りません。

バックグラウンド

このアルゴリズムは、ルジャンドルの3乗定理を利用します。これは、次の形式ではないすべての自然数n

                                                                   n = 4 ** a *(8b + 7)

3つの正方形の合計として表すことができます。

アルゴリズムは次のことを行います。

  1. 番号をとして表してください4**i * j

  2. 最大の整数見つけkようk**2 <= jj - k**2満たすルジャンドルの3平方の定理の仮説を。

  3. 設定しi = 0ます。

  4. j - k**2 - (i / 252)**2 - (i % 252)**2が完全な正方形かどうかを確認します。

  5. そうでない場合は、増分iしてステップ4に戻ります。

$ TIME='%e s' time golfscript legendre.gs <<< 0
[0 0 0 0]
0.02 s
$ TIME='%e s' time golfscript legendre.gs <<< 123456789
[32 0 38 11111]
0.02 s
$ TIME='%e s' time golfscript legendre.gs <<< 999999999
[45 1 217 31622]
0.03 s
$ TIME='%e s' time golfscript legendre.gs <<< 805306368
[16384 0 16384 16384]
0.02 s

タイミングは、Intel Core i7-4700MQに対応しています。

使い方

~              # Interpret the input string. Result: “n”
{              #
  .            # Duplicate the topmost stack item.
  [            #
    4*         # Multiply it by four.
    {          #
      4/       # Divide by four.
      ..       # Duplicate twice.
      4%1$     # Compute the modulus and duplicate the number.
      !|!      # Push 1 if both are truthy.
    }do        # Repeat if the number is divisible by four and non-zero.
  ]            # Collect the pushed values (one per iteration) into an array.
  )\           # Pop the last element from the array and swap it with the array.
}:r~           # Save this code block as “r” and execute it.
,(2\?          # Get the length of the array, decrement it and exponentiate.
:f;            # Save the result in “f”.
               # The topmost item on the stack is now “j”, which is not divisible by 
               # four and satisfies that n = f**2 * j.
{              #
  {..*}:^~     # Save a code block to square a number in “^” and execute it.
  4-1??        # Raise the previous number to the power of 1/4.
               # The two previous lines compute (x**2)**(1/4), which is sqrt(abs(x)).
  n*,          # Repeat the string "\n" that many times and compute its length.
               # This casts to integer. (GolfScript doesn't officially support Rationals.)
}:v~           # Save the above code block in “v” and execute it.
)..            # Undo the first three instructions of the loop.
{              #
   ;;(         # Discard two items from the stack and decrement.
   .^3$\-      # Square and subtract from “n”.
   r;)8%!      # Check if the result satisfies the hypothesis of the three-square theorem.
}do            # If it doesn't, repeat the loop.
-1...          # Push 0 (“i”) and undo the first four instructions of the loop.
{              #
  ;;;)         # Discard two items from the stack and increment “i”.
  ..252/@252%  # Push the digits of “i” in base 252.
  ^@^@+4$\-    # Square both, add and subtract the result 
  v^@-         # Take square root, square and compare.
}do            # If the difference is a perfect square, break the loop.
5$]            # Duplicate the difference an collect the entire stack into an array.
{f*}%          # Multiply very element of the array by “f”.
-4>            # Reduce the array to its four last elements (the four numbers).
`              # Convert the result into a string.

1
分かりませんでしたj-k-(i/252)-(i%252)。あなたのコメントから(私は実際にコードを読むことはできません)、それはあなたが意味するように見えますj-k-(i/252)^2-(i%252)^2。ところで、j-k-(i/r)^2-(i%r)^2r = sqrt(k)が少数の文字を保存する可能性がある(および私のCプログラムでk = 0でも問題なく動作するようです。)
Level River St

@steveverrill:はい、私は間違いを犯しました。気づいてくれてありがとう。あるはずですj-k^2-(i/252)^2-(i%252)^2。0が有効な入力であるかどうかをOPが明確にするのを待っています。あなたのプログラムは1414 -nan 6 4.000000入力を提供します0
デニス

私は、まだ投稿していないLegendreの定理を使用した私の新しいプログラムについて話しています。k = 0と同等の場合、%または/を使用してコードを呼び出さないようです。これが問題を引き起こさない理由です。投稿すると表示されます。古いプログラムを実行してくれてうれしいです。rev 1で完全な2GBテーブルを作成するためのメモリはありましたか?
レベルリバーセント

ええ、Cコンパイラは最適化の際に予想外の動作をする可能性があります。GolfScriptで、0/=>クラッシュ!:Pラップトップ(i7-4700MQ、8 GiB RAM)でrev 1を実行しました。平均して、実行時間は18.5秒です。
デニス

テーブルの作成を含めて18.5秒ですか?私のマシンでは2分以上かかります。問題はWindowsのメモリ管理にあることがわかります。プログラムに必要な2GBをすぐに与えるのではなく、小さなチャンクで提供するため、2GBが完全に割り当てられるまで、不必要に多くのスワッピングを行わなければなりません。実際には、ユーザー入力ごとに回答を検索する方がはるかに高速です。これは、プログラムがメモリを要求する必要がないためです。
レベルリバーセント

1

改訂1:C、190

a,z,m;short s[15<<26];p(){m=s[a=z-a];printf("%d %f ",m,sqrt(a-m*m));}
main(){m=31727;for(a=m*m;--a;s[z<m*m?z:m*m]=a%m)z=a/m*(a/m)+a%m*(a%m);scanf("%d",&z);for(;a*!s[a]||!s[z-a];a++);p();p();}

これは、rev 0よりもさらに多くのメモリを消費します。同じ原則:2平方のすべての可能な合計(および2平方の合計ではない数値の場合はゼロ)に対して正の値を持つテーブルを作成し、検索します。

このrev shortではchar、ヒットを格納する代わりにの配列を使用するため、フラグの代わりに正方形のペアの1つのルートをテーブルに格納できます。これによりp、ループの必要がないため、機能が大幅に簡素化されます(2つの平方の合計をデコードするため)。

Windowsのアレイには2GBの制限があります。short s[15<<26]1006632960要素の配列である、仕様に準拠するのに十分なラウンドを取得できます。残念ながら、プログラムの実行時の合計サイズはまだ2GBを超えており(OSの設定を調整しているにもかかわらず)、このサイズを超えて実行することはできません(理論的には可能ですが)。できることはshort s[14<<26](939524096要素)です。m*mでなければなりません厳密にこれよりも小さい(30651 ^ 2 = 939483801。)それでも、プログラムは完全に実行され、この制限のないOSで動作するはずです。

未ゴルフコード

a,z,m;
short s[15<<26];     
p(){m=s[a=z-a];printf("%d %f ",m,sqrt(a-m*m));}      
main(){       
 m=31727;             
 for(a=m*m;--a;s[z<m*m?z:m*m]=a%m)   //assignment to s[] moved inside for() is executed after the following statement. In this rev excessively large values are thrown away to s[m*m].
   z=a/m*(a/m)+a%m*(a%m);            //split a into high and low half, calculate h^2+l^2.                                  
 scanf("%d",&z); 
 for(;a*!s[a]||!s[z-a];a++);         //loop until s[a] and s[z-a] both contain an entry. s[0] requires special handling as s[0]==0, therefore a* is included to break out of the loop when a=0 and s[z] contains the sum of 2 squares.
 p();                                //print the squares for the first sum of 2 squares 
 p();}                               //print the squares for the 2nd sum of 2 squares (every time p() is called it does a=z-a so the two sums are exchanged.) 

Rev 0 C、219

a,z,i,m;double t;char s[1<<30];p(){for(i=t=.1;(m=t)-t;i++)t=sqrt(a-i*i);printf("%d %f ",i-1,t);}
main(){m=1<<15;for(a=m*m;--a;){z=a/m*(a/m)+a%m*(a%m);s[z<m*m?z:0]=1;}scanf("%d",&z);for(;1-s[a]*s[z-a];a++);p();a=z-a;p();}

これは記憶に飢えた獣です。1GBの配列を取り、2つの正方形のすべての可能な合計を計算し、配列内のそれぞれのフラグを格納します。次に、ユーザー入力zについて、2つの正方形aとzaの2つの合計を配列で検索します。

関数はp次いで、2乗の和を作るために使用された元の正方形をreconsitutes az-aそれがあれば(及びそれらを印刷し、整数として各対の第一、二重のような第二有する 2つの文字が必要とされる全ての整数であることが、t>m=t。)

プログラムは、2乗和のテーブルを作成するのに数分かかります(これはメモリ管理の問題が原因だと思います。期待どおりにジャンプするのではなく、メモリの割り当てがゆっくりと増加しているのがわかります)。非常に迅速に回答を生成します(複数の数値を計算する場合、scanf以降のプログラムをループに入れることができます。

改変されていないコード

a,z,i,m;
double t;
char s[1<<30];                              //handle numbers 0 up to 1073741823
p(){
 for(i=t=.1;(m=t)-t;i++)t=sqrt(a-i*i);      //where a contains the sum of 2 squares, search until the roots are found
 printf("%d %f ",i-1,t);}                   //and print them. m=t is used to evaluate the integer part of t. 

main(){       
 m=1<<15;                                   //max root we need is sqrt(1<<30);
 for(a=m*m;--a;)                            //loop m*m-1 down to 1, leave 0 in a
   {z=a/m*(a/m)+a%m*(a%m);s[z<m*m?z:0]=1;}  //split a into high and low half, calculate h^2+l^2. If under m*m, store flag, otherwise throw away flag to s[0]
 scanf("%d",&z);
 for(;1-s[a]*s[z-a];a++);                   //starting at a=0 (see above) loop until flags are found for sum of 2 squares of both (a) and (z-a)
 p();                                       //reconsitute and print the squares composing (a)
 a=z-a;                                     //assign (z-a) to a in order to...
 p();}                                      //reconsitute and print the squares composing (z-a)  

出力例

最初は質問ごとです。2番目は検索が難しいものとして選ばれました。この場合、プログラムは8192 ^ 2 + 8192 ^ 2 = 134217728を検索する必要がありますが、テーブルが作成されると数秒しかかかりません。

123456789
0 2.000000 3328 10601.000000

805306368
8192 8192.000000 8192 24576.000000

sqrtのプロトタイプを追加してはいけませんか?
edc65

@ edc65 GCCコンパイラ(Linux用ですが、WindowsマシンにCygwin Linux環境がインストールされています)を使用しています。これは、#include <stdio.h>(scanf / printf用)または#include <math.h>(sqrt用)を置く必要がないことを意味します。必要なライブラリを自動的にリンクします。デニスに感謝しなければなりません(彼はこの質問codegolf.stackexchange.com/a/26330/15599で私に言った)今までで最高のゴルフのヒント。
レベルリバーセント

リンクされた質問にハンプ・ザ・ワンプスが登場した理由を私はすでに疑問に思っていました。:)ところで、GCCがWindowsで何を使用しているかはわかりませんが、GNUリンカーは、の有無にかかわらず、数学ライブラリを自動的にリンクしませんinclude。Linuxでコンパイルするには、フラグが必要です-lm
デニス

面白い@Dennisは、それが含まれないstdioいくつかの他のライブラリではなく、とmathさえしてinclude?コンパイラーフラグを立てれば理解できますが、includeとにかく必要ありませんか?まあそれは私のために働いているので、私は文句を言っていません、再び先端に感謝します。ところで、私はルジャンドルの定理を利用して完全に異なる答えを投稿したいと思っています(しかし、それはまだ使用されますsqrt。)
Level River St

-lmコンパイラではなくリンカに影響します。gcc「知っている」機能のプロトタイプを必要としないため、インクルードの有無にかかわらず動作します。ただし、ヘッダーファイルは関数プロトタイプではなく、関数自体を提供します。Linuxでは(明らかにWindowsではないようですが)、数学ライブラリlibmは標準ライブラリの一部ではないためld、リンクするように指示する必要があります。
デニス

1

Mathematica、138文字

そのため、edc65で指摘されているように、これにより特定の入力に対して負の虚数の結果(805306368など)が生成されるため、これは有効なソリューションではありません。私は今のところそれを残します、そして、もし私が本当に私の時間を嫌うなら、私は戻ってそれを修正しようとします。

S[n_]:=Module[{a,b,c,d},G=Floor@Sqrt@#&;a=G@n;b:=G[n-a^2];c:=G[n-a^2-b^2];d:=G[n-a^2-b^2-c^2];While[Total[{a,b,c,d}^2]!=n,a-=1];{a,b,c,d}]

または、押しつぶされていない:

S[n_] := Module[{a, b, c, d}, G = Floor@Sqrt@# &;
 a = G@n;
 b := G[n - a^2];
 c := G[n - a^2 - b^2];
 d := G[n - a^2 - b^2 - c^2];
 While[Total[{a, b, c, d}^2] != n, a -= 1];
 {a, b, c, d}
]

私はアルゴリズムをあまり厳しく見ていませんでしたが、これは同じ考えだと思います。明らかな解決策を思いつき、それが機能するまで微調整しました。10億から10億のすべての数値でテストしました... 私のマシンでは、テストに約100秒しかかかりません。

これの良い点は、b、c、およびdが遅延割り当てで定義されているため:=、aが減分されたときにそれらを再定義する必要がないことです。これにより、以前の余分な行がいくつか節約されました。さらにゴルフをして、余分な部品を入れ子にすることもできますが、ここに最初のドラフトがあります。

ああ、あなたはそれを実行S@123456789し、{S@#, Total[(S@#)^2]} & @ 123456789またはでテストできます# == Total[(S@#)^2]&[123456789]。徹底的なテストは

n=0;
AbsoluteTiming@ParallelDo[If[e != Total[(S@e)^2], n=e; Abort[]] &, {e, 1, 1000000000}]
n

以前にPrint []ステートメントを使用しましたが、呼び出されなかったにもかかわらず、かなり遅くなりました。図を移動します。


これは本当にきれいです!可能な限り最初の値以外のすべての値を取得するだけで十分であることに驚いています。ゴルフでn - a^2 - b^2 - c^2は、変数として保存し、d^2それと同等のチェックをする方がおそらく短いでしょう。
xnor

2
本当に機能しますか?入力805306368に対してどのようなソリューションが見つかりますか?
edc65

S [805306368] = {-28383、536 I、32 I、I}。ほら それはない、あなたがそれを合計するとき805306368を生成するが、明らかにこのアルゴリズムに問題があります。今のところこれを撤回する必要があると思います。それを指摘してくれてありがとう...
krs013

2
すべての失敗数は具体的には、フォームのように見える2の大きな力で割り切れるように見えるa * 4^(2^k)ためにk>=2それがSO 4のすべての権限を抽出した、a4の倍数ではない(ただし、偶数であってもよいです)。さらに、それぞれaが3 mod 4、またはそのような数値の2倍です。最も小さいのは192です。
xnor14年

1

Haskell 123 + 3 = 126

main=getLine>>=print.f.read
f n=head[map(floor.sqrt)[a,b,c,d]|a<-r,b<-r,c<-r,d<-r,a+b+c+d==n]where r=[x^2|x<-[0..n],n>=x^2]

事前に計算された正方形に対する単純な総当たり。

-Oコンパイルオプションが必要です(このために3文字追加しました)。最悪の場合999950883の場合、所要時間は1分未満です。

GHCでのみテストされています。


1

C:198文字

たぶん100文字以上に絞ることができます。このソリューションで私が気に入っているのは、最小量のジャンクであり、単なるforループであり、forループが行うべきことを実行することです(これはクレイジーなことです)。

i,a,b,c,d;main(n){for(scanf("%d",&n);a*a+b*b-n?a|!b?a*a>n|a<b?(--a,b=1):b?++b:++a:(a=b=0,--n,++i):c*c+d*d-i?c|!d?c*c>i|c<d?(--c,d=1):d?++d:++c:(a=b=c=d=0,--n,++i):0;);printf("%d %d %d %d",a,b,c,d);}

そして、かなりのふりをした:

#include <stdio.h>

int n, i, a, b, c, d;

int main() {
    for (
        scanf("%d", &n);
        a*a + b*b - n
            ? a | !b
                ? a*a > n | a < b
                    ? (--a, b = 1)
                    : b
                        ? ++b
                        : ++a
                : (a = b = 0, --n, ++i)
            : c*c + d*d - i
                ? c | !d
                    ? c*c > i | c < d
                        ? (--c, d = 1)
                        : d
                            ? ++d
                            : ++c
                    : (a = b = c = d = 0, --n, ++i)
                : 0;
    );
    printf("%d %d %d %d\n", a, b, c, d);
    return 0;
}

編集:すべての入力に対して十分な速さではありませんが、別のソリューションで戻ってきます。この三面作戦の混乱を今のままにしておきます。


1

改訂B:C、179

a,b,c,d,m=1,n,q,r;main(){for(scanf("%d",&n);n%4<1;n/=4)m*=2;
for(a=sqrt(n),a-=(3+n-a*a)%4/2;r=n-a*a-b*b-c*c,d=sqrt(r),d*d-r;c=q%256)b=++q>>8;
printf("%d %d %d %d",a*m,b*m,c*m,d*m);}

改善してくれた@Dennisに感謝します。以下の回答の残りは、rev Aから更新されていません。

改訂A:C、195

a,b,c,d,n,m,q;double r=.1;main(){scanf("%d",&n);for(m=1;!(n%4);n/=4)m*=2;a=sqrt(n);a-=(3+n-a*a)%4/2;
for(;(d=r)-r;q++){b=q>>8;c=q%256;r=sqrt(n-a*a-b*b-c*c);}printf("%d %d %d %d ",a*m,b*m,c*m,d*m);}

私の他の答えよりもはるかに速く、はるかに少ないメモリで!

これはhttp://en.wikipedia.org/wiki/Legendre%27s_three-square_theoremを使用します。次の形式ではない任意の数は、3つの正方形の合計として表すことができます(これを禁止形式と呼びます)。

4^a*(8b+7), or equivalently 4^a*(8b-1)

すべての奇数の正方形の数は形式で(8b+1)あり、すべての偶数の正方形の数は表面的に形式であることに注意してください4b。しかし、これは、すべての偶数がすべて形式であるという事実を隠してい4^a*(odd square)==4^a*(8b+1)ます。結果として、2^x-(any square number < 2^(x-1))奇数xは常に禁止された形式になります。したがって、これらの数とその倍数は難しいケースであり、そのため、ここでのプログラムの多くは最初のステップとして4のべき乗を分けています。

@xnorの答えで述べられN-a*aているように、の2つの連続した値に対して禁止形式にすることはできませんa。以下に、彼のテーブルの簡略化された形式を示します。4で除算した後N%40にできないという事実に加えて、には2つの可能な値しかないことに注意してください(a*a)%4

(a*a)%4= 01
        +--
       1|10
  N%4= 2|21  <- (N-a*a)%4
       3|32

そのため(N-a*a)、禁止された形式の値、つまり(N-a*a)%43または0の値を避ける必要があります。これはN、奇数と偶数の両方で同じように発生することはありません(a*a)

したがって、私のアルゴリズムは次のように機能します。

1. Divide out powers of 4
2. Set a=int(sqrt(N)), the largest possible square
3. If (N-a*a)%4= 0 or 3, decrement a (only once)
4. Search for b and c such that N-a*a-b*b-c*c is a perfect square

私は特にステップ3の方法が好きです。3を追加Nします(3+N-a*a)%4 =。3または2の場合は減分が必要です(ただし1または0ではありません)。 。

未ゴルフコード

単音forループqの値導出し、分割/モジュロを使用することbc、それからの。aバイトを節約するために256ではなく除数として使用しようとしましたが、の値がa正しくなく、プログラムがハングすることがありました。256は、分割の>>8代わりに使用できるため、最良の妥協案でした/256

a,b,c,d,n,m,q;double r=.1;
main(){
  scanf("%d",&n);
  for(m=1;!(n%4);n/=4)m*=2;
  a=sqrt(n);
  a-=(3+n-a*a)%4/2;
  for(;(d=r)-r;q++){b=q>>8;c=q%256;r=sqrt(n-a*a-b*b-c*c);}
  printf("%d %d %d %d ",a*m,b*m,c*m,d*m);}

出力

面白い癖は、2進数を入力するとN-(a*a)= 0になることです。しかし、プログラムは0%4= 0を検出し、次の正方形に減少します。結果として、正方形の数値入力は、の形式でない限り、常に小さな正方形のグループに分解され4^xます。

999999999
31621 1 161 294

805306368
16384 0 16384 16384

999950883
31621 1 120 221

1
0 0 0 1

2
1 0 0 1

5
2 0 0 1

9
2 0 1 2

25
4 0 0 3

36
4 0 2 4

49
6 0 2 3

81
8 0 1 4

121
10 1 2 4

すごい!入力ごとに0.003秒!これらの5文字を​​取り戻すことができます。1. m=1前に宣言しmainます。2. ステートメントで実行scanfforます。3.のfloat代わりに使用しますdouble。4. n%4<1は、よりも短い!(n%4)。5. printfのフォーマット文字列に廃止されたスペースがあります。
デニス


ヒントをありがとう!後で修正できるn-=a*aため、機能しませんa(100 + 7 = 107のように、いくつかの間違った答えを出し、少数のケースでハングします)。残りのすべてを含めました。短縮するのはいいことですprintfが、唯一の答えは言語を変えることだと思います。スピードの鍵は、aすぐに良い値に落ち着くことです。Cで書かれ、256 ^ 2未満の検索スペースで、これはおそらくここで最速のプログラムです。
レベルリバーセント

はい、ごめんなさい。printf他の場所にバルクを追加するマクロまたは配列を使用しないと、ステートメントを短くすることは困難に思えます。言語の変更は「簡単な」方法のようです。あなたのアプローチは、CJamで82バイトの重さがあります。
デニス

0

JavaScriptを- 175の191 176 173文字

ブルートフォースですが、高速です。

高速で編集しますが、厄介な入力には不十分です。4の乗算による削減の最初のステップを追加する必要がありました。

編集2 関数を取り除き、ループ内に出力し、強制終了出口

編集3 0は有効な入力ではありません

v=(p=prompt)();for(m=1;!(v%4);m+=m)v/=4;for(a=-~(q=Math.sqrt)(v);a--;)for(w=v-a*a,b=-~q(w);b--;)for(x=w-b*b,c=-~q(x);c--;)(d=q(x-c*c))==~~d&&p([m*a, m*b, m*c, m*d],a=b=c='')

ゴルフをしていない:

v = prompt();

for (m = 1; ! (v % 4); m += m) 
{
  v /= 4;
}
for (a = - ~Math.sqrt(v); a--;) /* ~ force to negative integer, changing sign lead to original value + 1 */
{
  for ( w = v - a*a, b = - ~Math.sqrt(w); b--;)
  {
    for ( x = w - b*b, c = - ~Math.sqrt(x); c--;)
    {
      (d = Math.sqrt(x-c*c)) == ~~d && prompt([m*a, m*b, m*c, m*d], a=b=c='') /* 0s a,b,c to exit loop */
    }
  }
}

出力例

123456789
11111,48,10,8

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