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..1000000000
는 O (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 번의 반복이 필요했습니다 . 최악의 경우 인 854382778 은 21 만 필요합니다 .
최종 파이썬 코드는 다음과 같습니다.
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 , 5 및 7 의 값 이 각각 평균 32 배 에 도달 한다는 것을 알 수 있습니다. 그러나 값 2 와 6 은 각각 48 배에 도달 합니다. 홀수 값에 2를 곱하면 평균적으로 33 % 적은 반복 횟수 로 솔루션을 찾을 수 있습니다. 재구성은 다음과 같습니다.
a 와 b 는 c 와 d 뿐만 아니라 동일한 패리티를 갖도록 주의를 기울여야 하지만, 해결책이 발견되면 적절한 순서가 보장됩니다.
불가능한 경로는 확인할 필요가 없습니다.
제 2 값 ( b)을 선택한 후 , 임의의 주어진 모듈로에 대해 가능한 2 차 잔기가 주어지면 용액이 존재하는 것이 불가능할 수있다. 어쨌든 확인하거나 다음 반복으로 넘어가는 대신 솔루션으로 이어질 수있는 최소량만큼 b 를 줄이면 ' b ' 값을 '수정'할 수 있습니다. 두 개의 수정 테이블은 이러한 값을 저장합니다. 하나는 b 이고 다른 하나는 c 입니다. 더 높은 모듈로를 사용하면 (보다 정확하게는 2 차 잔류 물이 적은 모듈로를 사용하여) 더 나은 개선 결과를 얻을 수 있습니다. 값 a 는 수정이 필요하지 않습니다. n 을 짝수 로 수정하여A는 유효합니다.