네 개의 사각형 함께


19

라그랑주의 4 제곱 정리는 자연수를 4 제곱의 합으로 나타낼 수 있음을 알려줍니다. 당신의 임무는 이것을 수행하는 프로그램을 작성하는 것입니다.

입력 : 자연수 (10 억 미만)

출력 : 제곱이 해당 숫자와 합한 4 개의 숫자 (순서는 중요하지 않음)

참고 : 무차별 대입 검색을 수행 할 필요는 없습니다! 여기여기에 자세한 내용이 있습니다 . 이 문제를 사소한 기능이 있다면 (허용 할 것입니다), 허용되지 않습니다. 자동 프라임 기능과 제곱근이 허용됩니다. 하나 이상의 표현이 있으면 아무 문제가 없습니다. 무차별 대입을 선택했다면 적절한 시간 내에 실행해야합니다 (3 분)

샘플 입력

123456789

샘플 출력 (괜찮음)

10601 3328 2 0
10601 3328 2

코드를 짧게 만들면 무차별 강제 수행 할 수 있습니까?
마틴 엔더

@ m.buettner 네,하지만 큰 숫자를 처리해야합니다
qwr

@ m.buettner 포스트, 10 억 아래 어떤 자연수 읽기
qwr

아 죄송합니다.
Martin Ender

2
이 경우
@Dennis 자연수

답변:


1

CJam, 50 바이트

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

나의 세번째 (그리고 마지막으로 약속한다) 대답. 이 접근법은 primo의 답변 에 크게 의존합니다 .

CJam 통역사 에서 온라인으로 사용해보십시오 .

용법

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

배경

  1. 프리모으로 업데이트 알고리즘을 본 후, 나는 했다 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, 우리는 그것을 표현할 수있는 p**(2a) * b곳 - p의 작은 주요 요인 N1 바이트 이상을 저장할 수는 -.

    li_mF0=~2/#:J_*/:N!_{;)__*N\-[{_mqi__*@\-}3*])}g+Jf*p
    
  4. 대신 분할 : 이전 수정은 우리가 약간 (알고리즘 자체를 건드리지 않고) 구현을 변경할 수 있습니다 N에 의해 p**(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) * bb의 배수가 아닌 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.           ";

벤치 마크

인텔 코어 i7-3770에서 최대 999,999,999까지 모든 양의 정수에 대해 5 가지 버전을 모두 실행했으며 실행 시간을 측정하고 솔루션을 찾는 데 필요한 반복 횟수를 계산했습니다.

다음 표는 단일 정수의 평균 반복 횟수와 실행 시간을 보여줍니다.

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. primo의 알고리즘은 정 수당 4 회 반복과 6.6 마이크로 초로 매우 빠릅니다.

  2. 시작하는 floor(sqrt(N))것이 더 합리적입니다. 남은 세 제곱의 합에 대한 값이 작기 때문입니다. 예상대로 1부터 시작하는 것이 훨씬 느립니다.

  3. 이것은 잘못 구현 된 좋은 아이디어의 고전적인 예입니다. 실제로 코드 크기를 줄이려면mF 정수를 고려한 에 의존 합니다 N. 버전 3은 버전 2보다 적은 반복이 필요하지만 실제로는 훨씬 느립니다.

  4. 알고리즘은 변경되지 않지만 버전 4는 훨씬 느립니다. 각 반복마다 추가 부동 소수점 나누기와 정수 곱셈을 수행하기 때문입니다.

  5. 입력의 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**a, N - 4**a = 4**(a + c) * (8 * d + 7)그래서 그것은 세 제곱의 합으로서 표현 될 수 없다. 따라서 k > 1최소한 p**a반복이 필요합니다.

    고맙게도 primo의 원래 알고리즘은 엄청나게 빠릅니다 N < 1,000,000,000. 내가 손으로 찾을 수있는 최악의 경우 265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1)는 6,145 반복이 필요합니다. 컴퓨터에서 실행 시간이 300ms 미만입니다. 평균적으로이 버전은 primo 알고리즘 구현보다 13.5 배 느립니다.


"대신 표현 N으로 4**a * b, 우리는 그것을 표현할 수 있습니다 p**(2a) * b." 이것은 실제로 개선 입니다. 나는 이것을 포함하고 싶었지만 훨씬 길었습니다 (이상은 가장 큰 완벽한 제곱 인수를 찾는 것입니다). "1로 시작하여 증가 시키면 4 바이트가 절약됩니다." 확실히 느리다. 지정된 범위의 런타임은 4-5 배입니다. "최대 999,999,999까지의 모든 양의 정수는 24.67 시간이 걸렸으며, 평균 실행 시간은 정 수당 0.0888 밀리 초입니다." 펄은 전체 범위를 크런치하는 데 2.5 시간 밖에 걸리지
않았습니다

@primo : 그렇습니다. 나누는 p**a것은 개선이지만 작은 것입니다. 가장 큰 완전 제곱수로 나누면 1부터 시작할 때 차이가 있습니다. 제곱근의 정수 부분에서 시작할 때 여전히 개선됩니다. 이를 구현하는 데 2 ​​바이트 만 추가로 소요됩니다. 심연의 처형 시간은 CJam이 아닌 나의 개선으로 인한 것 같습니다. 벽 시간을 측정하는 대신 반복 횟수를 계산하여 모든 알고리즘 (제안한 알고리즘 포함)에 대한 테스트를 다시 실행하겠습니다. 시간 얼마나 걸리는지 보자 ...
Dennis

가장 큰 제곱 요소를 찾는 데 2 ​​바이트가 더 필요합니까?! 어떤 종류의 마법입니까?
primo

@primo : 정수가 스택에 있으면 1\1 (누적 기)으로 바꾸고 mF인수 분해를 푸시하고 {~2/#*}/모든 소인수를 지수를 2로 나눈 값으로 올린 다음 누산기와 곱합니다. 알고리즘을 직접 구현하기 위해 2 바이트 만 추가합니다. 작은 차이가 CJam는이 (것)하지 않기 때문에 어색한 방식 I, 4의 지수를 찾을 수 있었다 주로 기인 동안 ... 루프
데니스

어쨌든 벤치 마크는 끝났다. 최대 제곱 인수를 찾지 않고 1,000,000 개의 정수를 모두 분해하는 데 필요한 총 반복 횟수는 4,004,829,417이며 실행 시간은 1.83 시간입니다. 최대 제곱 인수로 나누면 반복 횟수가 3,996,724,799로 감소하지만 시간은 6.7 시간으로 증가합니다. 팩토리얼 화는 사각형을 찾는 것보다 더 많은 시간이 걸린 것 같습니다 ...
Dennis

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에서 수행 할 수 있습니다.)

힌트

코드는 다음 의사 파이썬과 동일합니다.

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

매스 매 티카 61 66 51

세 가지 방법이 표시됩니다. 첫 번째 방법 만 시간 요구 사항을 충족합니다.


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 개의 정수 파티션

또한 작동하지만 속도 요구 사항을 충족하기에는 너무 느립니다.

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]]바깥 괄호를 제거합니다. 솔루션은 하나의 요소 집합으로 반환되었습니다.


f[123456]

{348, 44, 20, 4}


3- 전력 표현

PowerRepresentations모든 솔루션을 4 제곱 문제로 반환 합니다 . 또한 다른 힘의 합계를 풀 수 있습니다.

PowersRepresentations는 5 초 안에 123456789를 4 제곱의 합으로 표현하는 181 가지 방법을 반환합니다.

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

솔

그러나 다른 합계에는 너무 느립니다.


와우, Mathematica는 무차별 대입을 빠르게합니다 IntegerPartitions는 세트의 DFT 컨볼 루션과 같이 모든 조합을 시도하는 것보다 훨씬 영리한 일을합니까? 그런데 사양은 숫자가 아닌 사각형을 요구합니다.
xnor

나는 티카는 무력을 사용 생각하지만, 아마 최적화했다 IntegerPartitions. 타이밍에서 볼 수 있듯이 속도는 첫 번째 (가장 큰) 숫자가의 제곱근에 가까운 지 여부에 따라 크게 달라집니다 n. 이전 버전에서 사양 위반을 발견해 주셔서 감사합니다.
DavidC

벤치마킹 할 수 f[805306368]있습니까? 먼저 4의 거듭 제곱으로 나누지 않고 999999999에 대해 0.05 초를 소비합니다. 5 분 후 805306368의 벤치 마크를 중단했습니다.
Dennis

f[805306368]{16384, 16384, 16384}21 분 후에 돌아옵니다 . {4} 대신 {3}을 (를) 사용했습니다. 0이 아닌 제곱의 합으로 해결하려는 시도는 몇 시간 동안 실행 한 후에 실패했습니다.
DavidC

Mathematica에 액세스 할 수는 없지만 설명서 센터에서 읽은 내용에서도 IntegerPartitions[n,4,Range[Floor@Sqrt@n]^2잘 작동합니다. 그러나 질문에 지정된 시간 제한을 준수하지 않기 때문에 점수 1에 방법 1을 사용해야한다고 생각하지 않습니다.
Dennis

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"

shebang을 1 바이트로 계산하고 가로 방향으로 줄 바꿈을 추가했습니다.

조합 제출의 무언가 .

평균 (최악의) 대소 문자 복잡도는 O (log n) O (n 0.07 ) 인 것 같습니다 . 내가 찾은 것은 0.001s보다 느리게 실행되지 않으며 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,이 두 번째입니다.

값의 범위를 테스트하려는 경우 다음 스크립트를 사용할 수 있습니다.

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 초 (초당 71000 값)이며 범위 999000000..1000000000O (log n) 평균 복잡도 와 일치하여 약 20 초 (초당 50000 값)가 걸립니다 .


최신 정보

편집 :이 알고리즘은 적어도 1 세기 동안 정신 계산기가 사용한 알고리즘과 매우 유사합니다 .

원래 게시 한 이후 1..1000000000 범위의 모든 값을 확인 했습니다 . '최악의 경우'행동은 값 699731569 로 나타 났으며 , 솔루션에 도착하기 전에 총 190 개의 조합 을 테스트했습니다 . 190 을 작은 상수로 생각한다면 확실히 필요한 범위에서 최악의 동작을 O (1) 로 간주 할 수 있습니다 . 즉, 거대한 테이블에서 솔루션을 찾는 것만 큼 빠르며 평균적으로 훨씬 빠릅니다.

그러나 다른 것. 190 번의 반복 후에 144400 보다 큰 것은 첫 번째 단계를 넘어서지 못했습니다. 너비 우선 탐색의 논리는 쓸모가 없습니다. 심지어 사용되지도 않습니다. 위의 코드는 상당히 짧아 질 수 있습니다.

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

검색의 첫 번째 패스 만 수행합니다. 그러나 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의 모든 값을 테스트하는 데 필요한 시간 이 18m 54에서 2m 41로 개선되었습니다. 최악의 경우 이전에는 190 번의 반복이 필요했습니다 . 최악의 경우 인 85438277821 만 필요합니다 .

최종 파이썬 코드는 다음과 같습니다.

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

여기에는 미리 계산 된 두 개의 수정 테이블 (하나는 10kb, 다른 하나는 253kb)이 사용됩니다. 위의 코드에는 이러한 테이블에 대한 생성기 함수가 포함되어 있지만 컴파일시 계산해야합니다.

: 더 겸손 크기의 보정 테이블 버전은 여기에서 찾을 수 있습니다 http://codepad.org/1ebJC2OV 이 버전의 평균 필요로 1.620 의 최악의 경우에, 학기당 반복 (38) , 및 전체 범위의 실행에 300 만 21S에 대해. 모듈로가 아닌 b 보정에 비트 단위 and를 사용하여 약간의 시간을 구성합니다 .


개량

짝수 값은 홀수 값보다 솔루션을 생성 할 가능성이 높습니다.
정신 계산 기사는 이전에 네 가지 요소를 모두 제거한 후에 분해 할 값이 짝수이면이 값을 2로 나눌 수 있고 솔루션을 재구성 할 수 있다고 언급했습니다.

이것은 정신 계산에는 의미가 있지만 (작은 값은 계산하기 쉬운 경향이 있지만) 알고리즘 적으로는 의미가 없습니다. 256 개의 랜덤 4 튜플 을 취하고 제곱 모듈로 8 의 합을 검사하면 1 , 3 , 57 의 값 이 각각 평균 32 배 에 도달 한다는 것을 알 수 있습니다. 그러나 값 26 은 각각 48 배에 도달 합니다. 홀수 값에 2를 곱하면 평균적으로 33 % 적은 반복 횟수 로 솔루션을 찾을 수 있습니다. 재구성은 다음과 같습니다.

abcd 뿐만 아니라 동일한 패리티를 갖도록 주의를 기울여야 하지만, 해결책이 발견되면 적절한 순서가 보장됩니다.

불가능한 경로는 확인할 필요가 없습니다.
제 2 값 ( b)을 선택한 후 , 임의의 주어진 모듈로에 대해 가능한 2 차 잔기가 주어지면 용액이 존재하는 것이 불가능할 수있다. 어쨌든 확인하거나 다음 반복으로 넘어가는 대신 솔루션으로 이어질 수있는 최소량만큼 b 를 줄이면 ' b ' 값을 '수정'할 수 있습니다. 두 개의 수정 테이블은 이러한 값을 저장합니다. 하나는 b 이고 다른 하나는 c 입니다. 더 높은 모듈로를 사용하면 (보다 정확하게는 2 차 잔류 물이 적은 모듈로를 사용하여) 더 나은 개선 결과를 얻을 수 있습니다. 값 a 는 수정이 필요하지 않습니다. n 을 짝수 로 수정하여A는 유효합니다.


1
이것은 믿기 힘든 일이야! 최종 알고리즘은 아마도 모든 대답 중에서 가장 단순하지만 190 번의 반복 만 있으면됩니다.
Dennis

@Dennis 다른 곳에서 언급되지 않은 경우 매우 놀랍습니다. 간과하기에는 너무 간단한 것 같습니다.
primo

1. 궁금합니다 : 복잡도 분석의 테스트 값 중 폭이 가장 넓은 순회가 필요합니까? 2. 당신이 연결 한 Wikipedia 기사는 약간 혼란 스럽다. Rabin-Shallit 알고리즘에 대해서는 언급하지만 완전히 다른 알고리즘에 대한 예를 제공합니다. 3. Rabin-Shallit 알고리즘이 정확히 당신의 성능을 능가하는시기를 보는 것은 흥미로울 것입니다. 필자는 우선 성 테스트가 실제로 비용이 많이 든다고 생각합니다.
데니스

1. 하나도 아닙니다. 2. 여기에서 내 정보를 얻습니다 (예 :이 알고리즘이 존재 함). 나는 분석을 보지 못했고 심지어 신문을 읽지도 않았다. 3. 1e60 부근에서 곡선이 너무 가파르게되어 O (log²n) 가 얼마나 '느리게'중요하든 상관없이 , 그 지점에서 여전히 교차합니다.
primo

1
질문의 두 번째 링크는 Rabin-Shallit을 구현하는 방법을 설명하지만 복잡성에 대해서는 다루지 않습니다. MathOverflow에 대한이 답변 은 논문의 요약을 제공합니다. 그건 그렇고, 당신은 1911 년 Gottfried Ruckle이 사용하는 알고리즘을 재발견했습니다 ( link ).
Dennis

6

파이썬 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 개의 제곱의 합으로 표현할 수 있어야합니다. 여기서 4 개 제곱은 가능한 최대 값 a=int(N**0.5)이거나 1보다 작은 것입니다. 를 돌보다. 검색 공간이 크게 줄어 듭니다.

나중에이 코드가 항상 해결책을 찾는 증거가 있습니다. 우리 는 세 제곱의 합인 a것을 찾고 싶습니다 n-a^2. 에서 르장 드르의 세 광장 정리 는 형태가 아닌, 다수의 세 제곱의 합이다 4^j(8*k+7). 특히, 이러한 숫자는 0 또는 3 (모듈로 4)입니다.

우리는 연속적인 두 개의 보여 a남은 금액을 만들 수는 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

어떤 두 개의 연속 있기 때문에 a주고 (N-a*a)%4 in [0,3], 그들 중 하나 사용하는 것이 안전합니다. 따라서 우리 nn^2<=N, 및 로 가능한 최대를 탐욕스럽게 사용합니다 n-1. 이후 N<(n+1)^2, 나머지는 N-a^2세 제곱합 기껏 같이 표현 될 (n+1)^2 -(n-1)^2같아지는, 4*n. 따라서 최대 2*sqrt(n)범위 까지의 값만 확인하면됩니다 R.

단일 솔루션 이후에 중지하고 마지막 값을 반복하지 않고 계산 d하고 값 중에서 만 검색 하여 실행 시간을 더욱 최적화 할 수 있습니다 b<=c<=d. 그러나 이러한 최적화가 없어도 최악의 인스턴스는 내 컴퓨터에서 45 초 안에 완료됩니다.

"x in R"의 체인은 불행합니다. (a, b, c, d)를 인코딩하는 단일 인덱스를 반복하여 문자열 대체 또는 대체로 단축 될 수 있습니다. itertools를 가져 오는 것은 그만한 가치가없는 것으로 판명되었습니다.

편집 : 인수를 더 깨끗하고 동일한 문자 수로 만들기 위해 int(2*n**.5)+1에서 2*int(n**.5)+2로 변경되었습니다 .


이것은 나를 위해 작동하지 않습니다 ...5 => (2, 1, 0, 0)
Harry Beadle

이상하게, 그것은 나를 위해 작동합니다 : 나는 5 => (2, 1, 0, 0)Ideone 3.2.3 또는 유휴 3.2.2에서 실행됩니다. 무엇을 얻습니까?
xnor

1
@xnor BritishColour가 가져옵니다 5 => (2, 1, 0, 0). 댓글을 읽었습니까? (이제 코드 스 니펫이있는 행에 3 개의 주석이 있습니다. 계속 진행 할 수 있습니까?)
Justin

@Quincunx 우리가 해독하려면 5 => (2, 1, 0, 0)의미 2^2 + 1^2 + 0^2 + 0^2 = 5합니다. 네, 가능합니다.
Dr. Rebmu

1
Quincunx는 @BritishColour의 의견을 읽었으며 알 수있는 한 5 => (2, 1, 0, 0)정확합니다. 문제의 예는 0 ^ 2 = 0을 유효한 제곱수로 간주합니다. 따라서 나는 영국 색상이 다른 것을 얻었을 것이라고 (내가 생각한 것처럼) 해석했습니다. 당신이 다시 응답하지 않은 영국의 색상, 우리는 당신이 실제로 얻을 것이라고 가정 할 수 2,1,0,0있습니까?
Level River St

5

CJam , 91 90 74 71 바이트

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

작지만 다른 접근 방식보다 느립니다.

온라인으로 사용해보십시오! 코드를 붙여 넣고 입력에 원하는 정수를 입력 하고 실행을 클릭 하십시오 .

배경

이 포스트는 99 바이트의 GolfScript 답변 으로 시작되었습니다 . 여전히 개선의 여지가 있었지만 GolfScript에는 내장 sqrt 기능이 없습니다. CJam 버전 과 매우 유사하기 때문에 GolfScript 버전을 개정 5 까지 유지했습니다 .

그러나 개정판 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

이것은 O (n) brute-force 인 Wikipedia 페이지의 알고리즘을 기반으로합니다.

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 초의 시간 제한이 있으므로 모든 숫자에 대해 작동하지 않을 수 있습니다.

배경

이 알고리즘은 활용 르장 드르의 세 평방 정리 모든 자연수한다고, n은 그 양식이 아닌

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

3 의 합으로 표현 될 수있다 제곱 .

알고리즘은 다음을 수행합니다.

  1. 숫자를로 표현하십시오 4**i * j.

  2. 가장 큰 정수 찾기 k같은 그 k**2 <= jj - k**2만족 르장 드르의 세 평방 정리의 가설을.

  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. BTW, 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.
Dennis

아직 게시하지 않은 Legendre의 정리를 사용하여 새 프로그램에 대해 이야기하고 있습니다. k = 0에 해당 할 때 % 또는 /로 코드를 호출하지 않는 것처럼 보이므로 문제가 발생하지 않습니다. 내가 게시하면 알 수 있습니다. 기존 프로그램을 실행하게되어 기쁩니다. rev 1에서 전체 2GB 테이블을 빌드 할 메모리가 있었으며 시간이 얼마나 걸렸습니까?
Level River St

예, C 컴파일러는 최적화 할 때 예기치 않게 동작 할 수 있습니다. GolfScript에서 0/=> 충돌! : P 랩탑 1 (i7-4700MQ, 8 GiB RAM)에서 rev 1을 실행했습니다. 평균적으로 실행 시간은 18.5 초입니다.
Dennis

와우는 테이블을 만드는 것을 포함하여 18.5 초입니까? 내 컴퓨터에서 2 분 이상 걸립니다. 문제가 Windows 메모리 관리라는 것을 알 수 있습니다. 프로그램에 2GB를 즉시 제공하는 대신 작은 덩어리로 제공하므로 전체 2GB가 할당 될 때까지 불필요한 스와핑을 많이 수행해야합니다. 실제로 사용자 입력 당 답변을 검색하는 것이 훨씬 빠릅니다. 프로그램이 메모리를 구걸 할 필요가 없기 때문입니다.
Level River St

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 제곱의 합이 아닌 숫자에 대해서는 0을 검색 한 다음 검색하십시오.

이 rev에서는 적중을 저장하는 short대신 배열을 사용 char하므로 플래그 대신 사각형 쌍 중 하나의 근을 테이블에 저장할 수 있습니다. 이것은 p루프가 필요하지 않기 때문에 (2 제곱의 합을 디코딩하기위한) 기능을 상당히 단순화합니다 .

Windows는 어레이에 2GB 제한이 있습니다. short s[15<<26]사양을 준수하기에 충분한 1006632960 요소의 배열 인 것을 알 수 있습니다 . 불행하게도, 전체 프로그램 런타임 크기가 2GB 이상 여전히 나는이 크기를 통해 실행할 수 없었다 (운영 체제 설정을 조정에도 불구하고) (이 이론적으로는 가능하지만.) 내가 할 수있는 최선이다 short s[14<<26](939,524,096 요소를.) m*m이어야합니다 그럼에도 불구하고 엄격히 이보다 낮습니다 (30651 ^ 2 = 939483801.) 그럼에도 불구하고 프로그램은 완벽하게 실행되며이 제한이없는 모든 OS에서 작동해야합니다.

Ungolfed 코드

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

개정 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의 두 합을 검색합니다.

함수는 p다음 2 제곱의 합계를 만들기 위해 사용 된 원래 사각형 reconsitutes az-a정수로서, 각 쌍의 제 및 그것들을 출력하는 더블으로서 제 2 (이 경우 가지고 두 개 이상의 문자를 필요로하는 모든 정수로, t>m=t .)

이 프로그램은 제곱합 테이블을 만드는 데 몇 분이 걸립니다 (메모리 관리 문제 때문이라고 생각합니다. 메모리 할당이 예상대로 점프하는 대신 느리게 증가하는 것을 볼 수 있습니다.) 그러나 일단 완료되면 답변을 매우 빠르게 생성합니다 (여러 개의 숫자를 계산할 경우 scanf이후부터 프로그램을 루프에 넣을 수 있음).

ungolfed 코드

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)  

출력 예

첫 번째는 질문 당입니다. 두 번째는 검색하기 어려운 것으로 선택되었습니다. 이 경우 프로그램은 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 )
레벨 리버 세인트

나는 왜 Wumpus Huntus가 관련 질문에 나타나는지 궁금했습니다. :) 그건 그렇고, 나는 GCC가 Windows에서 무엇을 사용하는지 알지 못하지만 GNU 링커는 수학 라이브러리를 include. Linux에서 컴파일하려면 플래그가 필요합니다-lm
Dennis

흥미로운 @Dennis, 그것은 포함 않는 stdio여러 가지 다른 라이브러리가 아니라와 math함께include? 컴파일러 플래그를 넣으면 include어쨌든 필요하지 않습니까? 글쎄, 그것은 나를 위해 일하고 있으므로, 나는 불평하지 않습니다. 팁에 다시 한번 감사드립니다. BTW 난 (하지만 여전히를 사용 르장 드르의 정리의 완전히 다른 답변을 복용 장점을 게시 할 바라고 있어요 sqrt.)
레벨 강 세인트

-lm컴파일러가 아닌 링커에 영향을줍니다. gcc"알고있는"기능에 대한 프로토 타입을 요구하지 않기 때문에 포함 여부에 관계없이 작동합니다. 그러나 헤더 파일은 함수 자체가 아니라 함수 프로토 타입 만 제공합니다. Linux (Windows 는 아님 )에서 수학 라이브러리 libm 은 표준 라이브러리의 일부가 아니므 ld로 링크하도록 지시 해야합니다.
Dennis

1

매스 매 티카, 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}. 허. 그 수행은 당신이 그것을 요약하면 805,306,368 생산하지만, 분명히이 알고리즘에 문제가있는 것입니다. 나는 지금 이것을 철회해야 할 것 같다; 지적 해 주셔서 감사합니다 ...
krs013

2
모두 실패한 숫자는 2의 거듭 제곱으로 나눌 수있는 것처럼 보입니다. 구체적으로, 4 의 배수를 모두 추출하여 4 의 배수가 아니더라도를 a * 4^(2^k)위한 형태 인 것 같습니다 (그러나 짝수 일 수 있음). 더욱이, 각각 은 3 mod 4 또는 그러한 수의 두 배이다. 가장 작은 것은 192입니다.k>=2aa
xnor

1

하스켈 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-loop, for-loop가 해야하는 일 (미친 것)입니다.

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*a2 연속 값에 대해 금지 된 형식을 사용할 수 없습니다 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 단계를 수행하는 방식이 마음 N에 듭니다.에 3을 추가하여(3+N-a*a)%4 = 3에 2를 더하여 3 또는 2 인 (1 또는 0은 아님). 이것을 2로 나누면 전체 작업을 매우 간단한 표현으로 수행 할 수 있습니다. .

Ungolfed 코드

단일 주 for루프 q의 값을 도출하고, 분할 / 모듈로 이용 b하고 c그것으로부터한다. a바이트를 절약하기 위해 256 대신 제수로 사용하려고 했지만 때로는 값 a이 맞지 않고 프로그램이 무한정 중단되었습니다. 분할 >>8대신 사용할 수 있으므로 256이 가장 타협했습니다 /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);}

산출

흥미로운 점은 제곱 숫자를 입력하면 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. 명세서 scanf에서 실행 하십시오 for. 3. float대신에 사용하십시오 double. 4. n%4<1보다 짧습니다 !(n%4). 5. printf 형식 문자열에 사용되지 않는 공간이 있습니다.
Dennis


팁 주셔서 감사합니다! 나중에 수정 될 수 n-=a*a있기 때문에 작동하지 않습니다 a(100 + 7 = 107과 같은 소수의 경우에 잘못된 답변을 제공하고 중단됩니다) 나머지는 모두 포함했습니다. 단축하는 것이 좋겠지 printf만 언어를 바꾸는 것이 유일한 해결책이라고 생각합니다. 속도의 핵심은 a신속하게 좋은 가치를 정하는 것 입니다. C로 작성되었으며 검색 공간이 256 ^ 2 미만인이 프로그램은 아마도 가장 빠른 프로그램 일 것입니다.
Level River St

알겠습니다 printf매크로 또는 배열을 사용하지 않으면 명령문을 단축하는 것이 어려워 보일 수 있습니다. 언어 변경은 "쉬운"방법으로 보입니다. CJam의 접근 방식은 82 바이트입니다.
Dennis

0

자바 스크립트 -175191176 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.