Dört kare birlikte


19

Lagrange'ın dört kare teoremi bize herhangi bir doğal sayının dört kare sayısının toplamı olarak temsil edilebileceğini söyler. Göreviniz bunu yapan bir program yazmaktır.

Girdi: Doğal bir rakam (1 milyarın altında)

Çıktı: Kareleri bu sayıya karşılık gelen dört sayı (sıralama önemli değildir)

Not: Kaba kuvvet araması yapmak zorunda değilsiniz! Ayrıntılar burada ve burada . Bu sorunu önemsizleştiren bir işlev varsa (belirleyeceğim), buna izin verilmez. Otomatik asal fonksiyonlara ve kareköküne izin verilir. Birden fazla temsil varsa, herhangi biri iyidir. Kaba kuvvet yapmayı seçtiyseniz, makul süre içinde çalışmalıdır (3 dakika)

örnek giriş

123456789

örnek çıktı (her ikisi de iyidir)

10601 3328 2 0
10601 3328 2

Kodumu daha kısa yaparsa ben kaba kuvvet yapabilir miyim?
Martin Ender

@ m.buettner Evet, ama çok sayıda
işlemeli

@ m.buettner Yazıyı okuyun, 1 milyarın altındaki doğal sayılar
qwr

Ah özür dilerim.
Martin Ender

2
@Dennis Bu sayıdaki doğal sayılar 0 içermez.
qwr

Yanıtlar:


1

CJam, 50 bayt

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

Üçüncü (ve son, söz veriyorum) cevabım. Bu yaklaşım ağırlıklı olarak primo cevabına dayanmaktadır .

CJam yorumlayıcısında çevrimiçi deneyin .

kullanım

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

Arka fon

  1. Primo'nun güncellenmiş algoritmasını gördükten sonra, bir CJam uygulamasının nasıl puan alacağını görmek zorunda kaldım :

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

    Sadece 58 bayt! Bu algoritma neredeyse sabit bir zamanda çalışır ve farklı değerleri için çok fazla değişiklik göstermez N. Bunu değiştirelim ...

  2. floor(sqrt(N))Başlamak ve azaltmak yerine, başlayabilir 1ve artırabiliriz . Bu 4 bayt tasarruf sağlar.

    li{W):W;:N4md!}g;0_{;)__*N\-[{_mqi__*@\-}3*])}g+2W#f*p
    
  3. NOlarak 4**a * bifade etmek yerine, bunu 1 bayt tasarruf etmek için p**(2a) * b- pen küçük asal faktör nerede - olarak ifade edebiliriz N.

    li_mF0=~2/#:J_*/:N!_{;)__*N\-[{_mqi__*@\-}3*])}g+Jf*p
    
  4. Yerine bölünmesi: Bir önceki modifikasyon bize biraz (algoritma kendisi dokunmadan) uygulamasını değiştirmeye olanak Ntarafından p**(2a)tarafından çarpın çözümü ve p**abiz doğrudan katları için olası çözümler kısıtlayabilirsiniz p**a. Bu, 2 bayt daha kazandırır.

    li:NmF0=~2/#:J!_{;J+__*N\-[{_mqJ/iJ*__*@\-}3*])}g+`
    
  5. İlk tamsayıyı katların katlarıyla sınırlamamak p**aek bir bayt kaydeder.

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

Son algoritma

  1. Bulun ave böyle ki N = p**(2a) * b, nerede bbir katı değildir p**2ve pen küçük asal faktördür N.

  2. Ayarlayın j = p**a.

  3. Ayarlayın k = floor(sqrt(N - j**2) / A) * A.

  4. Ayarlayın l = floor(sqrt(N - j**2 - k**2) / A) * A.

  5. Ayarlayın m = floor(sqrt(N - j**2 - k**2 - l**2) / A) * A.

  6. Varsa N - j**2 - k**2 - l**2 - m**2 > 0, ayarlayın j = j + 1ve 3. adıma geri dönün.

Bu aşağıdaki gibi uygulanabilir:

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

Deneyler

Intel Core i7-3770 üzerinde 999,999,999'a kadar tüm pozitif tamsayıların 5'ini çalıştırdım, yürütme süresini ölçtüm ve bir çözüm bulmak için gerekli yinelemeleri saydım.

Aşağıdaki tabloda tek bir tam sayı için ortalama yineleme sayısı ve yürütme süresi gösterilmektedir:

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. Tam sayı başına sadece 4 yineleme ve 6,6 mikro saniyede primo algoritması inanılmaz derecede hızlıdır.

  2. Başlamak floor(sqrt(N))daha mantıklı, çünkü bu bizi kalan üç karenin toplamı için daha küçük değerlerle bırakıyor. Beklendiği gibi, 1'den başlamak çok daha yavaştır.

  3. Bu, kötü uygulanmış iyi bir fikrin klasik bir örneğidir. Kod boyutunu gerçekten azaltmak için mF, tamsayıyı çarpanlarına güveniriz N. Sürüm 3, sürüm 2'den daha az yineleme gerektirmesine rağmen, uygulamada çok daha yavaştır.

  4. Algoritma değişmese de, sürüm 4 çok daha yavaştır. Bunun nedeni, her bir yinelemede ek bir kayan nokta bölümü ve bir tamsayı çarpımı gerçekleştirmesidir.

  5. Giriş için N = p**(2a) ** b, algoritma 5 (k - 1) * p**a + 1yinelemeleri gerektirir , burada kalgoritma 4'ün gerektirdiği yineleme sayısı. Eğer k = 1ya da a = 0, bu fark etmez.

    Bununla birlikte, formun herhangi bir girdisi 4**a * (4**c * (8 * d + 7) + 1)oldukça kötü performans gösterebilir. Başlangıç değeri olarak j = p**a, N - 4**a = 4**(a + c) * (8 * d + 7)bu yüzden üç kare bir toplamı olarak ifade edilemez. Böylece, k > 1en azından p**aiterasyonlar gereklidir.

    Neyse ki, primo'nun orijinal algoritması inanılmaz derecede hızlı ve N < 1,000,000,000. Elle bulabildiğim en kötü durum 265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1), 6.145 iterasyon gerektiriyor. Makinemde yürütme süresi 300 ms'den az. Ortalama olarak, bu sürüm primo algoritmasının uygulanmasından 13.5 kat daha yavaştır.


" NOlarak 4**a * bifade etmek yerine, olarak ifade edebiliriz p**(2a) * b." Bu aslında bir gelişme . Bunu dahil etmek isterdim, ama çok daha uzundu (ideal en büyük mükemmel kare faktörünü bulmaktır). "1 ile başlayan ve artan 4 bayt tasarruf." Bu kesinlikle daha yavaş. Herhangi bir aralık için çalışma süresi 4-5 kat daha uzundur. "999,999,999'a kadar olan tüm pozitif tamsayılar 24,67 saat sürdü ve tamsayı başına ortalama 0,0888 milisaniye çalışma süresi verdi." Perl, tüm aralığı kırmak için sadece 2.5 saat sürdü ve Python çevirisi 10 kat daha hızlı;)
primo

@primo: Evet, haklısın. Bölmek p**abir gelişmedir, ama küçük bir gelişmedir. En büyük mükemmel kare faktörüne bölmek , 1'den başlayarak büyük bir fark yaratır ; karekökün tamsayı kısmından başlarken hala bir gelişme. Bunu uygulamak sadece iki bayta mal olur. Uçsuz bucaksız infaz süresi CJam'dan değil, gelişmemden kaynaklanıyor gibi görünüyor. Duvar süresini ölçmek yerine yinelemeleri sayarak tüm algoritmalar için (önerdiğiniz algoritma dahil) testleri tekrar çalıştıracağım. En ne kadar görelim o ... alır
Dennis

En büyük kare faktörünü bulmak sadece 2 ek bayttır ?! Bu ne tür bir büyü?
primo

@primo: Eğer tamsayı yığın üzerindeyse, 1\1 (akümülatör) ile değiştirir, mFçarpanlarına ayırır ve {~2/#*}/her asal faktörü üssüne ikiye böler , ardından akümülatör ile çarpar. Algoritmanızın doğrudan uygulanması için, bu sadece 2 bayt ekler. Küçük fark esas olarak 4'ün üssünü bulmak zorunda olduğum garip yoldan kaynaklanıyor, çünkü CJam bir süre döngüsüne sahip değil (görünüyor) ...
Dennis

Her neyse, karşılaştırma bitti. En büyük kare faktörünü bulmadan 1.000.000 tamsayıyı çarpanlarına ayırmak için gereken toplam yineleme sayısı, 1.83 saatlik bir yürütme süresi ile 4.004.829.417'dir. En büyük kare faktörüne bölünmesi yineleme sayısını 3.996.724.799'a düşürür, ancak zamanı 6.7 saate çıkarır. Faktoring, kareleri bulmaktan çok daha fazla zaman alıyor gibi görünüyor ...
Dennis

7

FRACTRAN: 156 98 kesir

Bu klasik bir sayı teorisi sorunu olduğundan, bunu çözmek için sayıları kullanmaktan daha iyi bir yol var!

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 formunun girdisini alır ve 3 a × 5 b × 7 c × 11 d çıktı verir . Gerçekten iyi bir tercümanınız varsa 3 dakika içinde çalışabilir . Olabilir.

... tamam, pek değil. FRACTRAN'da bunu denemek zorunda olduğum kadar eğlenceli bir problem gibi görünüyordu . Açıkçası, bu zaman gereksinimleri (kaba kuvvetler) yapmaz ve zar zor bile golf gibi soruya uygun bir çözüm değil, ama ben burada göndereceğini düşündüm çünkü bir Codegolf sorusu her gün değil FRACTRAN'da yapılabilir;)

İpucu

Kod, aşağıdaki sözde Python ile eşdeğerdir:

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

Üç yöntem gösterilmiştir. Sadece ilk yaklaşım zaman gereksinimini karşılar.


1-FindInstance (51 karakter)

Bu denklemin tek bir çözümünü verir.

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

Örnekler ve zamanlamalar

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

Bu da işe yarıyor, ancak hız gereksinimini karşılamak için çok yavaş.

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

Range[0, Floor@Sqrt@n]^2, karenin kökünden n(bölümdeki olası en büyük kare) daha küçük olan tüm kareler kümesidir .

{4}nyukarıda belirtilen karelerden oluşan 4 elemandan oluşan tamsayı bölümleri gerektirir .

1, fonksiyon içinde IntegerPartitionsilk çözümü döndürür.

[[1]]dış parantezleri kaldırır; çözelti, bir elemanlık bir set halinde geri döndürüldü.


f[123456]

{348, 44, 20, 4}


3-PowerRepresentations

PowerRepresentations tüm çözümleri 4 kareye döndürür . Diğer güçlerin toplamı için de çözebilir.

PowersRepentations, 5 saniyenin altında 123456789'u 4 karenin toplamı olarak ifade etmenin 181 yolunu döndürür:

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

sollar

Ancak, diğer meblağlar için çok yavaştır.


Vay canına, Mathematica kaba kuvveti hızlı yapıyor. IntegerPartitions, setlerdeki DFT evrişimi gibi her kombinasyonu denemekten çok daha akıllıca bir şey yapıyor mu? Özellikler bu arada karelerini değil sayıları ister.
xnor

Mathematica'nın kaba kuvvet kullandığını düşünüyorum, ancak muhtemelen optimize etti IntegerPartitions. Zamanlamalardan görebileceğiniz gibi, hız, ilk (en büyük) sayının kareköküne yakın olmasına bağlı olarak büyük ölçüde değişir n. Önceki sürümdeki spesifikasyon ihlalini yakaladığınız için teşekkür ederiz.
DavidC

Kıyaslama yapabilir misiniz f[805306368]? İlk önce 4'lük güçlere bölünmeden, benim çözümüm 999999999 için 0.05 s alır; Ben 5 dakika sonra benchmark 805306368 için iptal ...
Dennis

f[805306368]{16384, 16384, 16384}21 dakika sonra döner . {4} yerine {3} kullandım. Toplamı sıfır olmayan 4 kare ile çözme girişimi birkaç saat süren çalışmadan sonra başarısız oldu.
DavidC

Mathematica'ya erişimim yok, ancak dokümantasyon merkezinde okuduğumdan da IntegerPartitions[n,4,Range[Floor@Sqrt@n]^2çalışmalı. Ancak, puanınız için yöntem 1'i kullanmanız gerektiğini düşünmüyorum, çünkü soruda belirtilen zaman sınırına uymuyor.
Dennis

7

Perl - 116 bayt 87 bayt (aşağıdaki güncellemeye bakın)

#!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"

Mesele bir bayt olarak sayılır, yatay akıl için yeni satırlar eklenir.

Bir kombinasyonu şey gönderme.

Ortalama (en kötü?) Vaka karmaşıklığı O (log n) O (n 0.07 ) gibi görünmektedir . Bulduğum hiçbir şey 0.001'den daha yavaş çalışmadı ve 900000000 - 999999999 arasındaki tüm aralığı kontrol ettim . Bundan daha uzun süren bir şey bulursanız, ~ 0.1s veya daha fazla, lütfen bana bildirin.


Örnek Kullanımı

$ 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

Bunların son ikisi diğer başvurular için en kötü senaryo gibi görünmektedir. Her iki durumda da, gösterilen çözüm tam anlamıyla kontrol edilen ilk şeydir. Çünkü 123456789bu ikinci.

Bir değer aralığını test etmek isterseniz, aşağıdaki komut dosyasını kullanabilirsiniz:

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

Bir dosyaya aktarıldığında en iyisi. Aralık 1..1000000, bilgisayarımda yaklaşık 14 saniye 999000000..1000000000sürer ( saniyede 71000 değer) ve aralık , O (günlük n) ortalama karmaşıklıkla tutarlı olarak yaklaşık 20 saniye (saniyede 50000 değer) sürer .


Güncelleme

Düzenleme : Bu algoritma zihinsel hesap makineleri tarafından en az bir yüzyıl boyunca kullanılan bir algoritmaya çok benzer olduğu ortaya çıkıyor .

Başlangıçta yayınladığımdan beri, 1..1000000000 aralığındaki her değeri kontrol ettim . 'En kötü durum' davranışı, bir çözüme ulaşmadan önce toplam 190 kombinasyonu test eden 699731569 değeriyle sergilendi . 190'ın küçük bir sabit olduğunu düşünürseniz - ve kesinlikle yaparsam - gerekli aralıktaki en kötü durum davranışı O (1) olarak düşünülebilir . Yani, çözümü dev bir tablodan aramak kadar hızlı ve ortalama olarak oldukça hızlı.

Yine de başka bir şey. 190 iterasyondan sonra , 144400'den daha büyük bir şey ilk geçişin ötesine bile geçmedi. Genişlik ilk geçişinin mantığı değersizdir - hatta kullanılmaz. Yukarıdaki kod biraz kısaltılabilir:

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

Hangi sadece aramanın ilk geçişini gerçekleştirir. Yine de , ikinci geçişe ihtiyaç duyan 144400'ün altında herhangi bir değer olmadığını doğrulamamız gerekiyor:

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

Kısacası, 1..1000000000 aralığı için, neredeyse sabit bir zaman çözümü mevcuttur ve buna bakıyorsunuz.


Güncelleme Güncellemesi

@Dennis ve ben bu algoritmayı geliştirdik. Aşağıdaki yorumlardaki ilerlemeyi ve ilginizi çekerse sonraki tartışmayı takip edebilirsiniz. Gerekli aralık için yineleme ortalama sayısı biraz üzerinde düşmüştür 4 aşağı 1.229 ve tüm değerleri test etmek için gerekli süre 1..1000000000 2m 41s kadar, 18m 54'lerinden yükseltilmiştir. En kötü durum daha önce 190 iterasyon gerektiriyordu ; Şimdi en kötü durum, 854382778 , sadece 21'e ihtiyaç duyuyor .

Son Python kodu şudur:

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

Bu, biri 10kb, diğeri 253kb olmak üzere önceden hesaplanmış iki düzeltme tablosu kullanır. Yukarıdaki kod, bu tablolar için jeneratör işlevlerini içerir, ancak bunlar derleme zamanında hesaplanmalıdır.

Daha mütevazı boyutta düzeltme tablolarına sahip bir sürüm burada bulunabilir: http://codepad.org/1ebJC2OV Bu sürüm, en kötü 38 ile her dönem için ortalama 1.620 yineleme gerektirir ve tüm aralık yaklaşık 3m 21s'de çalışır. Modulo yerine b düzeltmesi için bitsel kullanılarak biraz zaman oluşur .and


İyileştirmeler

Değerlerin bile tek bir değerden daha fazla çözüm üretme olasılığı daha yüksektir.
Daha önce bahsedilen zihinsel hesaplama makalesi, dört faktörün tüm faktörlerini kaldırdıktan sonra, ayrıştırılacak değerin eşit olması durumunda, bu değerin ikiye bölünebileceğini ve çözümün yeniden yapılandırılabileceğini not eder:

Bu zihinsel hesaplama için anlamlı olsa da (daha küçük değerlerin hesaplanması daha kolay olma eğilimindedir), algoritmik olarak fazla bir anlam ifade etmez. Eğer alırsan 256 rastgele 4 -tuples ve karelerinin toplamını incelemek modulo 8 , hangi değerlerin olduğunu göreceksiniz 1 , 3 , 5 ve 7 her ortalama ulaşılır 32 kez. Ancak, 2 ve 6 değerlerinin her birine 48 kez ulaşılır . Tek değerlerin 2 ile çarpılması , ortalama % 33 daha az yinelemede bir çözüm bulacaktır . İmar aşağıdaki gibidir:

A ve b'nin c ve d ile aynı pariteye sahip olmasına dikkat edilmelidir , ancak bir çözüm bulunmuşsa, uygun bir siparişin var olduğu garanti edilir.

İmkansız yolların kontrol edilmesi gerekmez.
İkinci değer olan b'yi seçtikten sonra, herhangi bir modülo için olası ikinci dereceden kalıntılar göz önüne alındığında, bir çözümün mevcut olması zaten imkansız olabilir. Yine de kontrol etmek veya bir sonraki yinelemeye geçmek yerine , b'nin değeri, muhtemelen bir çözüme yol açabilecek en küçük miktarda azaltılarak 'düzeltilebilir'. İki düzeltme tablosu, biri b için diğeri c için bu değerleri saklar . Daha yüksek bir modulo kullanmak (daha kesin olarak, daha az kuadratik kalıntıya sahip bir modulo kullanmak) daha iyi bir iyileşme ile sonuçlanacaktır. Değer bir herhangi bir düzeltme ihtiyacı yoktur; n'yi eşit olacak şekilde değiştirerek ,a geçerlidir.


1
Bu inanılmaz! Son algoritma muhtemelen tüm cevapların en basitidir, ancak 190 yineleme
Dennis

@Dennis Başka bir yerde belirtilmemiş olsaydı çok şaşırırdım. Aşırı bakılmış olmak çok basit görünüyor.
primo

1. Merak ediyorum: Karmaşıklık analizinizdeki test değerlerinden herhangi biri önce genişlikli geçişi gerektiriyor mu? 2. Bağlantı verdiğiniz Wikipedia makalesi biraz kafa karıştırıcı. Rabin-Shallit algoritmasından bahseder, ancak tamamen farklı bir algoritma için bir örnek sağlar. 3. Rabin-Shallit algoritmasının sizinkinden tam olarak ne zaman daha iyi performans gösterdiğini görmek ilginç olurdu, ilköğretim testlerinin pratikte oldukça pahalı olduğunu hayal ediyorum.
Dennis

1. Bir değil. 2. Burada bilgilerimi aldım (yani bu algoritma var); Analizi görmedim, hatta gazeteyi bile okumadım. 3. Eğri 1e60 civarında o kadar dikleşir ki, O'nun (log²n) ne kadar 'yavaş' olduğu önemli değildir , yine de bu noktadan geçecektir.
primo

1
Sorudaki ikinci bağlantı Rabin-Shallit'in nasıl uygulanacağını açıklıyor, ancak karmaşıklık hakkında konuşmuyor. MathOverflow hakkındaki bu cevap makalenin güzel bir özetini vermektedir. Bu arada, 1911'de Gottfried Ruckle tarafından kullanılan bir algoritmayı yeniden keşfettiniz ( bağlantı ).
Dennis

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

Girdiyi N4 ile bölünemez hale getirdikten sonra , bunlardan biri mümkün olan en büyük değer a=int(N**0.5)veya bundan daha az olan dört karenin toplamı olarak ifade edilebilir olmalı ve diğer üç karenin toplamı için sadece küçük bir kalan bırakmalıdır. a göz kulak olmak. Bu, arama alanını büyük ölçüde azaltır.

İşte daha sonra bu kod her zaman bir çözüm bulur kanıt. Biz bulmak isteyen aSo n-a^2üç kareler toplamıdır. Gönderen Legendre Üç Kare Teoremi o formu olmadıkça, bir üç numaralı kareler toplamıdır 4^j(8*k+7). Özellikle, bu sayılar ya 0 ya da 3'tür (modulo 4).

aArt arda kalan hiçbir ikisinin arta kalan miktarın N-a^2her iki ardışık değer için böyle bir şekle sahip olamayacağını gösteriyoruz. Bunu, sadece 4'ün tüm güçlerini çıkardığımız için ave Nmodulo 4'ün bir tablosunu yaparak yapabiliriz .N%4!=0N

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

Art arda iki tane avermediği (N-a*a)%4 in [0,3]için bunlardan biri kullanmak güvenlidir. Böylece, ve nile mümkün olan en büyük değeri kullanıyoruz . Çünkü , üç karenin toplamı olarak temsil edilecek kalan en fazladır, ki bu eşittir . Bu nedenle, sadece aralığa kadar olan değerleri kontrol etmek yeterlidir . n^2<=Nn-1N<(n+1)^2N-a^2(n+1)^2 -(n-1)^24*n2*sqrt(n)R

Kişi, tek bir çözümden sonra durarak, son değeri yinelemek yerine hesaplayarak dve yalnızca değerler arasında arama yaparak çalışma süresini daha da optimize edebilir b<=c<=d. Ancak, bu optimizasyonlar olmasa bile, bulabildiğim en kötü örnek, makinemde 45 saniyede bitti.

"R için x için" zinciri talihsizdir. Muhtemelen dize ikamesi ya da kodlayan tek bir indeks (a, b, c, d) üzerinden yineleme ile kısaltılabilir. Itertools'u içe aktarmak buna değmez.

Düzenleme: Değişkeni daha temiz ve aynı karakter sayısını sağlamak için int(2*n**.5)+1olarak değiştirildi 2*int(n**.5)+2.


Bu benim için işe yaramıyor ...5 => (2, 1, 0, 0)
Harry Beadle

Garip, benim için 5 => (2, 1, 0, 0)çalışıyor: Ideone 3.2.3 veya Idle 3.2.2'de koşuyorum. Siz ne alırsınız?
xnor

1
@xnor BritishColour alır 5 => (2, 1, 0, 0). Yorumu bile okudun mu? (Şimdi bu kod snippet'inin üst üste 3 yorumu var. Çizgiyi devam ettirebilir miyiz?)
Justin

@Quincunx Eğer deşifre edeceksek 5 => (2, 1, 0, 0)bu demektir 2^2 + 1^2 + 0^2 + 0^2 = 5. Evet, yapabiliriz.
Dr. Rebmu

1
Quincunx, @ BritishColour'un yorumunu okudum ve görebildiğim kadarıyla 5 => (2, 1, 0, 0)doğru. Sorudaki örnekler 0 ^ 2 = 0 değerini geçerli bir kare numarası olarak kabul eder. Bu nedenle (xnor'ın yaptığı gibi) British Color'ın başka bir şey bulduğunu yorumladım. İngiliz rengi, bir daha cevap vermediğiniz için, aslında yaptığınızı varsayabilir miyiz 2,1,0,0?
Level River St

5

CJam , 91 90 74 71 bayt

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

Kompakt, ancak diğer yaklaşımımdan daha yavaş.

Çevrimiçi deneyin! Kodu yapıştırın, Giriş alanına istediğiniz tamsayıyı yazın ve Çalıştır'a tıklayın .

Arka fon

Bu gönderi 99 bayt GolfScript yanıtı olarak başladı . Hala iyileştirme için yer olsa da, GolfScript yerleşik sqrt işlevinden yoksundur. CJAM sürümüne çok benzediğinden GolfScript sürümünü revizyon 5'e kadar sakladım.

Ancak, revizyon 6'dan bu yana yapılan optimizasyonlar GolfScript'te bulunmayan operatörler gerektirdiğinden, her iki dil için ayrı açıklamalar göndermek yerine, daha az rekabetçi (ve çok daha yavaş) sürümü bırakmaya karar verdim.

Uygulanan algoritma sayıları kaba kuvvetle hesaplar:

  1. Girdi için mbulun Nve Wöyle m = 4**W * N.

  2. Ayarlayın i = 257**2 * floor(sqrt(N/4)).

  3. Ayarlayın i = i + 1.

  4. Tamsayılar bul j, k, löyle ki i = 257**2 * j + 257 * k + l, nereye k, l < 257.

  5. d = N - j**2 - k**2 - l**2Mükemmel bir kare olup olmadığını kontrol edin .

  6. Değilse ve 3. adıma geri dönün.

  7. Yazdırın 2**W * j, 2**W * k, 2**W * l, 2**W * sqrt(m).

Örnekler

$ 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

Zamanlamalar Intel Core i7-4700MQ'ya karşılık gelir.

Nasıl çalışır

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

Bu, Wikipedia sayfasındaki O (n) kaba kuvvet olan algoritmaya dayanır.

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 bayt

~{.[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>`

Hızlı, ama uzun. Yeni satır kaldırılabilir.

Çevrimiçi deneyin. Çevrimiçi yorumlayıcının 5 saniyelik bir zaman sınırı olduğunu unutmayın, bu nedenle tüm sayılar için çalışmayabilir.

Arka fon

Algoritma, Legendre'in formdaki olmayan her doğal sayının n olduğunu belirten üç kare teoreminden yararlanır.

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

üç karenin toplamı olarak ifade edilebilir .

Algoritma şunları yapar:

  1. Numarayı olarak ifade edin 4**i * j.

  2. En büyük tamsayı bulun köyle ki k**2 <= jve j - k**2tatmin Legendre üç kare teoremin hipotezi.

  3. Ayarlayın i = 0.

  4. j - k**2 - (i / 252)**2 - (i % 252)**2Mükemmel bir kare olup olmadığını kontrol edin .

  5. Değilse, artırın ive 4. adıma geri dönün.

Örnekler

$ 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

Zamanlamalar Intel Core i7-4700MQ'ya karşılık gelir.

Nasıl çalışır

~              # 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
Anlamadım j-k-(i/252)-(i%252). (Aslında kodu okuyamıyorum) yorumlarınızdan, demek istediğiniz gibi görünüyor j-k-(i/252)^2-(i%252)^2. BTW, j-k-(i/r)^2-(i%r)^2burada r = sqrt (k) birkaç karakter kaydedebilir (ve C programımda k = 0 için bile sorunsuz çalışıyor gibi görünüyor.)
Level River St

@steveverrill: Evet, bir hata yaptım. Fark ettiğin için teşekkürler. Olmalı j-k^2-(i/252)^2-(i%252)^2. Ben hala OP 0 geçerli bir giriş olup olmadığını netleştirmek için bekliyor. Programınız 1414 -nan 6 4.000000girdi verir 0.
Dennis

Henüz yayınlamadığım Legendre teoremini kullanarak yeni programımdan bahsediyorum. Hiçbir zaman% veya / veya k = 0 eşdeğeri olduğunda kodu çağırır gibi görünüyor, bu yüzden sorunlara neden değil. Ne zaman gönderdiğimi göreceksin. Eski programımı çalıştırdığına sevindim. Rev 1'de tam 2GB masayı oluşturmak için belleğiniz var mıydı ve ne kadar sürdü?
Level River St

Evet, C derleyicisi optimizasyon sırasında beklenmedik şekilde davranabilir. GolfScript'te 0/=> kilitlenme! : P Rev 1'inizi dizüstü bilgisayarımda çalıştırıyorum (i7-4700MQ, 8 GiB RAM). Ortalama olarak, yürütme süresi 18.5 saniyedir.
Dennis

Wow masayı inşa etmek de dahil 18.5 saniye mi? Makinemde 2 dakikadan fazla sürüyor. Sorunun Windows bellek yönetimi olduğunu görebiliyorum. Programa hemen ihtiyaç duyduğu 2GB'yi vermek yerine, küçük parçalar halinde verir, bu nedenle tam 2GB tahsis edilene kadar çok fazla gereksiz takas yapmalıdır. Aslında kullanıcı girişi başına cevap aramak çok daha hızlıdır, çünkü o zamana kadar programın bellek için dilenmek zorunda kalması gerekmez.
Level River St

1

Rev 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();}

Bu, rev 0'dan daha fazla bellek açtır. Aynı ilke: 2 karenin tüm olası toplamları için pozitif bir değere sahip bir tablo oluşturun (ve iki karenin toplamı olmayan sayılar için sıfır), sonra arayın.

Bu devirde isabetleri saklamak shortyerine bir dizi kullanın char, böylece kareler çiftlerinden birinin kökünü sadece bir bayrak yerine tabloda saklayabilirim. Bu p, bir döngüye gerek olmadığından işlevi (2 karenin toplamının kodunu çözmek için) önemli ölçüde basitleştirir .

Windows'un dizilerde 2GB sınırı vardır. Ben short s[15<<26]1006632960 elemanları bir dizi olan yuvarlak alabilirim , spec uygun. Ne yazık ki, toplam program çalışma zamanı boyutu 2GB üzerinde hala ve bu boyutu üzerinde çalıştırmak mümkün olmamıştır (OS ayarlarını değiştirmeyi rağmen) (teorik olarak mümkün olsa.) Yapabileceğim en iyisidir short s[14<<26](939524096 unsurları.) m*mOlmalıdır kesinlikle bundan daha az (30651 ^ 2 = 939483801.) Bununla birlikte, program mükemmel çalışır ve bu kısıtlamaya sahip olmayan herhangi bir işletim sisteminde çalışmalıdır.

Kod çözülmemiş kod

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

Bu bir anı aç canavardır. 1 GB'lık bir dizi alır, 2 karenin tüm olası toplamlarını hesaplar ve dizideki her biri için bir bayrak saklar. Daha sonra z kullanıcı girişi için, diziyi a ve za olmak üzere 2 kareden oluşan iki toplamı arar.

fonksiyonu p, daha sonra 2 kareleri toplamı yapmak için kullanılan özgün kareler reconsitutes ave z-abir tamsayı olarak, her bir çiftin birinci ve yazdırır, bir çift ikinci (bu eğer var iki karakter gerekli olan tüm tamsayı olduğu, t> m=t.)

Program kareler toplamları tablosu oluşturmak için birkaç dakika sürer (Ben bu bellek yönetimi sorunları nedeniyle olduğunu düşünüyorum, bellek tahsisi beklendiği gibi yukarı atlamak yerine yavaş gidiyor görüyorum.) Ancak bir kez yapılır çok hızlı cevaplar üretir (eğer birkaç sayı hesaplanacaksa, başından scanfitibaren program bir döngüye sokulabilir).

çözülmemiş kod

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)  

Örnek çıktı

Birincisi soru başına. İkincisi, araştırılması zor olan seçildi. Bu durumda, program 8192 ^ 2 + 8192 ^ 2 = 134217728'e kadar arama yapmak zorundadır, ancak tablo oluşturulduktan sonra yalnızca birkaç saniye sürer.

123456789
0 2.000000 3328 10601.000000

805306368
8192 8192.000000 8192 24576.000000

Sqrt için bir prototip eklememeniz gerekir mi?
edc65

@ edc65 GCC derleyicisini kullanıyorum (Linux içindir, ancak Windows makineme Cygwin Linux ortamı yükledim.) Bu, #include <stdio.h>(scanf / printf için) veya #include <math.h>(sqrt için ) koymam gerekmediği anlamına geliyor . gerekli kütüphaneleri otomatik olarak bağlar. Bunun için Dennis'e teşekkür etmeliyim (bana bu soru hakkında codegolf.stackexchange.com/a/26330/15599 ) En iyi golf ipucunu anlattım.
Level River St

Bağlantılı sorularda Wumpus Avı'nın neden ortaya çıktığını merak ediyordum. :) Bu arada, GCC'nin Windows'ta ne kullandığını bilmiyorum, ancak GNU bağlayıcı matematik kütüphanesini otomatik olarak veya olmadan bağlamaz include. Linux üzerinde derleme için, bayrak ihtiyaç-lm
Dennis

İlginç @Dennis, bu içermez stdiodiğer bazı kütüphaneler, ancak ve mathhatta birlikteinclude ? Derleyici bayrağını koyarsanız, includeyine de ihtiyacınız yok mu? Benim için çalışıyor, bu yüzden şikayet etmiyorum, bahşiş için tekrar teşekkürler. BTW Legendre teoreminden faydalanarak tamamen farklı bir cevap göndermeyi umuyorum (ama yine de bir sqrt. Kullanacaktır )
Level River St

-lmderleyiciyi değil, bağlayıcıyı etkiler. gcc"bildiği" fonksiyonlar için prototip gerektirmemeyi tercih eder, bu yüzden içerdiği veya içermediği şekilde çalışır. Ancak, üstbilgi dosyaları yalnızca işlev prototipleri sağlar, işlevlerin kendileri değil. Linux'ta (görünüşe göre Windows'da değil), matematik kütüphanesi libm standart kütüphanelerin bir parçası değildir, bu yüzden ldona bağlanmak için talimat vermeniz gerekir .
Dennis

1

Mathematica, 138 karakter

Dolayısıyla, bunun edc65 (örneğin, 805306368) tarafından işaret edildiği gibi belirli girdiler için negatif ve hayali sonuçlar ürettiği ortaya çıkıyor, bu yüzden bu geçerli bir çözüm değil. Şimdilik bırakacağım ve belki de zamanımdan gerçekten nefret edersem, geri dönüp düzeltmeye çalışacağım.

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

Veya sorulmamış:

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

Algoritmalara çok fazla bakmadım, ama bunun aynı fikir olduğunu umuyorum. Sadece bariz bir çözüm buldum ve işe yarayana kadar ayarladım. 1 ile bir milyar arasındaki tüm sayılar için test ettim ve ... işe yarıyor. Test makinemde sadece 100 saniye sürüyor.

Bununla ilgili güzel olan şey, b, c ve d'nin gecikmeli atamalar :=ile tanımlandığı için a azaldığında yeniden tanımlanmaları gerekmez. Bu, daha önce sahip olduğum birkaç ekstra satırı kurtardı. Daha fazla golf yapabilir ve gereksiz parçaları yuvalayabilirim, ama ilk taslak burada.

Oh, ve onu şöyle çalıştırıyorsun S@123456789ve {S@#, Total[(S@#)^2]} & @ 123456789veya ile test edebilirsin # == Total[(S@#)^2]&[123456789]. Kapsamlı test

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

Daha önce bir Print [] ifadesi kullandım, ancak hiç çağrılmasa bile bu çok yavaşladı. Git şekil.


Bu gerçekten temiz! Mümkün olduğunca büyük olan ilk değeri almanın yeterli olduğuna şaşırıyorum. Golf için, n - a^2 - b^2 - c^2bir değişken olarak kaydetmek ve d^2buna eşit olup olmadığını kontrol etmek muhtemelen daha kısadır .
xnor

2
Gerçekten işe yarıyor mu? 805306368 girişi için hangi çözümü bulur?
edc65

S [805306368] = {- 28383,536 I, 32 I, I}. Huh. Bu , toplamda 805306368 üretir, ancak açıkçası bu algoritmada bir sorun vardır. Şimdilik bunu geri çekmem gerekecek; işaret ettiğiniz için teşekkürler ...
krs013 21:14

2
Bütün başarısız sayılar Özellikle, bunlar formun görünmektedir 2. büyük güçler tarafından bölünebilir olduğu görülüyor a * 4^(2^k)için k>=2böylece 4 tüm güçlerini dışarı çıkarılan ettikten, a4 kişilik bir katı değil (ancak bu bile olabilir). Ayrıca, her abiri ya 3 mod 4 ya da böyle bir sayının iki katıdır. En küçük olanı 192.
xnor

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]

Önceden hesaplanmış karelere göre basit kaba kuvvet.

-ODerleme seçeneğine ihtiyacı var (bunun için 3 karakter ekledim). En kötü durum 999950883 için 1 dakikadan az sürer.

Sadece GHC'de test edilmiştir.


1

C: 198 karakter

Muhtemelen 100'den fazla karaktere kadar sıkıştırabilirim. Bu çözümle ilgili sevdiğim şey, minimum bir önemsiz miktardır, sadece düz bir döngü için, bir döngü için ne yapmalı (ki bu çılgınca).

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

Ve çok güzelce:

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

Düzenleme: Tüm girişler için yeterince hızlı değil, ama başka bir çözüm ile döneceğim. Bu üçlü operasyon karmaşasının şu andan itibaren kalmasına izin vereceğim.


1

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

Geliştirmeler için @Dennis'e teşekkürler. Aşağıdaki cevabın geri kalanı Rev A'dan güncellenmemiştir.

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

Diğer cevabımdan çok daha hızlı ve daha az hafıza ile!

Bu, http://en.wikipedia.org/wiki/Legendre%27s_three-square_theorem kullanır . Aşağıdaki formda olmayan herhangi bir sayı 3 karenin toplamı olarak ifade edilebilir (buna yasaklanmış form diyorum):

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

Tüm tek kare sayıların formda (8b+1)ve tüm çift kare sayıların yüzeysel olarak formda olduğunu unutmayın 4b. Ancak bu, tüm kare sayıların bile formda olduğu gerçeğini gizler 4^a*(odd square)==4^a*(8b+1). Sonuç olarak 2^x-(any square number < 2^(x-1))garipx her zaman yasaklanmış formda olacaktır. Bu yüzden bu sayılar ve katları zor vakalardır, bu yüzden buradaki programların çoğu 4'üncü güçleri ilk adım olarak ayırır.

@ Xnor cevabında belirtildiği gibi, N-a*a2 ardışık değeri için yasaklanmış formda olamaz a. Aşağıda masasının basitleştirilmiş bir formunu sunuyorum. 4'e bölündükten sonra N%40'a eşit olamayacağına ek olarak, için sadece 2 olası değer olduğunu unutmayın (a*a)%4.

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

Bu nedenle, bunun (N-a*a)yasaklanmış formda, yani (N-a*a)%43 veya 0 olduğu değerlerden kaçınmak istiyoruz. Görülebileceği gibi, bu Nhem tek hem de çift için aynı olamaz.(a*a) .

Yani, algoritmam şöyle çalışıyor:

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

Özellikle 3. adımı yapma şeklimi seviyorum. 3 veya 2 Nolursa (3+N-a*a)%4 =(1 veya 0 değil) azaltmanın gerekli olması için 3'ü ekliyorum. Bunu 2'ye bölün ve tüm iş oldukça basit bir ifade ile yapılabilir .

Kod çözülmemiş kod

Tek Not fordöngü qdeğerlerini türetmek için ve bölme / modülo kullanımını bve condan. aBaytları kurtarmak için 256 yerine bölen olarak kullanmayı denedim , ancak bazen değeri adoğru değildi ve program muhtemelen süresiz olarak asılı kaldı. 256, bölme >>8yerine kullanabileceğim en iyi uzlaşma oldu /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);}

Çıktı

İlginç bir tuhaflık, bir kare numarası girerseniz N-(a*a)= 0. Ancak program bunu 0%4= 0 olarak algılar ve bir sonraki kareye doğru azalır. Sonuç olarak, kare numarası girişleri formda olmadıkları sürece her zaman daha küçük kareler grubuna ayrılır 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

İnanılmaz! Her giriş için 0.003 s! Bu 5 karakteri geri alabilirsiniz: 1. Daha m=1önce beyan edin main. Yürütme 2. scanfyılında foraçıklamada. 3. floatyerine kullanın double. 4. n%4<1daha kısadır !(n%4). 5. printf'in biçim dizesinde eski bir alan var.
Dennis


İpuçları için teşekkürler! n-=a*açalışmaz, çünkü adaha sonra değiştirilebilir (bazı yanlış cevaplar verir ve 100 + 7 = 107 gibi az sayıda durumda asılı kalır.) Geri kalanları dahil ettim. Kısaltmak bir şey için iyi olurdu printfama bence tek cevap dili değiştirmek. Hızın anahtarı, hızlı bir şekilde iyi bir değere yerleşmektir a. C ile yazılmış ve 256 ^ 2'den daha az bir arama alanına sahip, bu muhtemelen buradaki en hızlı program.
Level River St

Doğru, özür dilerim. İfadeyi kısaltmak, printfbaşka bir yerde toplu ekleyecek bir makro veya dizi kullanmadan zor görünüyor. Dil değiştirmek "kolay" bir yol gibi görünüyor. Yaklaşımınız CJam'da 82 bayt ağırlığında olacak.
Dennis

0

JavaScript - 175 191 176 173 karakter

Kaba kuvvet, ama hızlı.

Hızlı Düzenle ancak bazı kötü girişler için yeterli değil. İlk 4 azaltma adımını eklemek zorunda kaldım.

2 Düzenle Fonksiyondan kurtulun, döngü içerisinden çıktı alın ve çıkma zorunluluğunu zorlayın

Düzenle 3 0 geçerli bir giriş değil

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='')

Ungolfed:

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 */
    }
  }
}

Örnek çıktı

123456789
11111,48,10,8

805306368
16384,16384,16384,0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.