Twierdzenie Lagrange'a o czterech kwadratach mówi nam, że dowolna liczba naturalna może być reprezentowana jako suma czterech liczb kwadratowych. Twoim zadaniem jest napisanie programu, który to robi.
Dane wejściowe: liczba naturalna (poniżej 1 miliarda)
Wynik: cztery liczby, których kwadraty sumują się do tej liczby (kolejność nie ma znaczenia)
Uwaga: nie musisz szukać brutalnej siły! Szczegóły tutaj i tutaj . Jeśli istnieje funkcja, która trywializuje ten problem (ustalę), nie jest dozwolona. Automatyczne funkcje pierwsze i pierwiastek kwadratowy są dozwolone. Jeśli jest więcej niż jedna reprezentacja, wszystko jest w porządku. Jeśli zdecydujesz się na brutalną siłę, musi ona działać w rozsądnym czasie (3 minuty)
przykładowe dane wejściowe
123456789
próbka wyjściowa (każda z nich jest w porządku)
10601 3328 2 0
10601 3328 2
Odpowiedzi:
CJam, 50 bajtów
Moja trzecia (i ostatnia, obiecuję) odpowiedź. To podejście opiera się w dużej mierze na odpowiedzi primo .
Wypróbuj online w interpretatorze CJam .
Stosowanie
tło
Po obejrzeniu zaktualizowanego algorytmu primo musiałem zobaczyć, jak uzyskałaby implementacja CJam:
Tylko 58 bajtów! Ten algorytm działa w prawie stałym czasie i nie wykazuje dużej zmienności dla różnych wartości
N
. Zmieńmy to ...Zamiast zaczynać
floor(sqrt(N))
i zmniejszać, możemy zacząć od1
przyrostu. To oszczędza 4 bajty.Zamiast wyrażać
N
jako4**a * b
, możemy wyrazić to jakop**(2a) * b
- gdziep
jest najmniejszy czynnik głównyN
- aby zaoszczędzić 1 bajt.Poprzedniego modyfikacja pozwala nam nieznacznie zmienić realizację (bez dotykania samego algorytmu): Zamiast dzielenia
N
przezp**(2a)
i pomnożyć przez rozwiązaniep**a
, możemy bezpośrednio ograniczają możliwych rozwiązań do wielokrotnościp**a
. To oszczędza 2 kolejne bajty.Nie ograniczanie pierwszej liczby całkowitej do wielokrotności
p**a
zapisuje dodatkowy bajt.Ostateczny algorytm
Znajdź
a
ib
takie, żeN = p**(2a) * b
gdzieb
nie jest wielokrotnościąp**2
ip
jest najmniejszym czynnikiem podstawowymN
.Set
j = p**a
.Set
k = floor(sqrt(N - j**2) / A) * A
.Set
l = floor(sqrt(N - j**2 - k**2) / A) * A
.Set
m = floor(sqrt(N - j**2 - k**2 - l**2) / A) * A
.Jeśli
N - j**2 - k**2 - l**2 - m**2 > 0
, ustawj = j + 1
i wróć do kroku 3.Można to zaimplementować w następujący sposób:
Benchmarki
Uruchomiłem wszystkie 5 wersji dla wszystkich liczb całkowitych dodatnich do 999,999,999 na moim rdzeniu Intel Core i7-3770, zmierzyłem czas wykonania i policzyłem iteracje wymagane do znalezienia rozwiązania.
Poniższa tabela pokazuje średnią liczbę iteracji i czas wykonania dla pojedynczej liczby całkowitej:
Przy zaledwie 4 iteracjach i 6,6 mikro sekundy na liczbę całkowitą algorytm primo jest niewiarygodnie szybki.
Rozpoczęcie od
floor(sqrt(N))
ma większy sens, ponieważ pozostawia nam to mniejsze wartości dla sumy pozostałych trzech kwadratów. Zgodnie z oczekiwaniami, rozpoczęcie od 1 jest znacznie wolniejsze.To klasyczny przykład źle zaimplementowanego dobrego pomysłu. Aby faktycznie zmniejszyć rozmiar kodu, polegamy na tym
mF
, co rozkłada liczbę całkowitąN
. Chociaż wersja 3 wymaga mniej iteracji niż wersja 2, w praktyce jest znacznie wolniejsza.Chociaż algorytm się nie zmienia, wersja 4 jest znacznie wolniejsza. Wynika to z tego, że wykonuje ona dodatkowy podział zmiennoprzecinkowy i mnożenie liczb całkowitych w każdej iteracji.
Dla danych wejściowych
N = p**(2a) ** b
algorytm 5 będzie wymagał(k - 1) * p**a + 1
iteracji, gdziek
jest liczba wymaganych algorytmów 4. Jeślik = 1
luba = 0
, to nie ma znaczenia.Jednak każde wprowadzanie formularza
4**a * (4**c * (8 * d + 7) + 1)
może działać dość źle. W odniesieniu do wartości wyjściowejj = p**a
,N - 4**a = 4**(a + c) * (8 * d + 7)
tak że nie może zostać wyrażona jako suma trzech pól. Zatem wymagane sąk > 1
przynajmniejp**a
iteracje.Na szczęście oryginalny algorytm primo jest niesamowicie szybki i
N < 1,000,000,000
. Najgorszy przypadek, jaki udało mi się znaleźć ręcznie, to265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1)
6,145 iteracji. Czas realizacji jest mniejszy niż 300 ms na moim komputerze. Ta wersja jest średnio 13,5 razy wolniejsza niż implementacja algorytmu primo.źródło
N
jako4**a * b
, możemy wyrazić to jakop**(2a) * b
”. To właściwie poprawa . Chciałbym to uwzględnić, ale było to o wiele dłużej (idealne jest znalezienie największego idealnego współczynnika kwadratowego). „Zaczynając od 1 i zwiększając oszczędzasz 4 bajty”. To zdecydowanie wolniej. Czas działania dla dowolnego zakresu jest 4-5 razy dłuższy. „Wszystkie dodatnie liczby całkowite do 999,999,999 trwały 24,67 godziny, co daje średni czas wykonania 0,0888 milisekund na liczbę całkowitą”. Perlowi zajęło tylko 2,5 godziny, aby przełamać cały zakres, a tłumaczenie na Python jest 10 razy szybsze;)p**a
jest poprawą, ale jest niewielki. Dzielenie przez największy idealny współczynnik kwadratowy robi dużą różnicę, zaczynając od 1; to wciąż poprawa, zaczynając od całkowitej liczby pierwiastka kwadratowego. Wdrożenie kosztowałoby tylko dwa dodatkowe bajty. Okropny czas wykonania wydaje się być spowodowany moimi ulepszeniami, a nie CJam. Ponownie przetestuję wszystkie algorytmy (w tym ten, który zaproponowałeś), licząc iteracje zamiast mierząc czas ściany. Zobaczmy, jak długo to zajmie ...1\
zamienia ją na 1 (akumulator),mF
przesuwa faktoryzację i{~2/#*}/
podnosi każdy współczynnik pierwszy do wykładnika podzielonego przez dwa, a następnie mnoży go przez akumulator. Do bezpośredniej implementacji twojego algorytmu, który dodaje tylko 2 bajty. Niewielka różnica jest głównie z powodu niewygodnej drodze musiałem znaleźć wykładnik 4, ponieważ CJam nie (wydaje się) mają while pętli ...FRACTRAN:
15698 frakcjiPonieważ jest to klasyczny problem teorii liczb, czy jest lepszy sposób na rozwiązanie tego niż używanie liczb!
Pobiera dane wejściowe formularza 2 n × 193 i dane wyjściowe 3 a × 5 b × 7 c × 11 d . Może biec za 3 minuty, jeśli masz naprawdę dobrego tłumacza. Może.
Wskazówka
Kod jest równoważny następującemu pseudo-Pythonowi:
źródło
Mathematica
61 6651Pokazane są trzy metody. Tylko pierwsze podejście spełnia wymagania czasowe.
1-FindInstance (51 znaków)
Zwraca to jedno równanie równania.
Przykłady i terminy
2-IntegerPartitions
Działa to również, ale jest zbyt wolne, aby spełnić wymagania dotyczące prędkości.
Range[0, Floor@Sqrt@n]^2
to zbiór wszystkich kwadratów mniejszy niż pierwiastek kwadratowy zn
(największy możliwy kwadrat w partycji).{4}
wymaga partycji całkowitychn
składających się z 4 elementów z wyżej wspomnianego zestawu kwadratów.1
, w ramach funkcjiIntegerPartitions
zwraca pierwsze rozwiązanie.[[1]]
usuwa zewnętrzne szelki; rozwiązanie zostało zwrócone jako zestaw jednego elementu.3-PowerRepresentations
PowerRepresentations zwraca wszystkie rozwiązania problemu 4 kwadratów. Może również rozwiązać sumy innych mocy.
PowersRepresentations zwraca, w mniej niż 5 sekund, 181 sposobów wyrażenia 123456789 jako sumę 4 kwadratów:
Jest jednak o wiele za wolny na inne kwoty.
źródło
IntegerPartitions
. Jak widać z czasów, prędkość różni się znacznie w zależności od tego, czy pierwsza (największa) liczba jest zbliżona do pierwiastka kwadratowego zn
. Dziękujemy za wykrycie naruszenia specyfikacji we wcześniejszej wersji.f[805306368]
? Nie dzieląc najpierw potęg 4, moje rozwiązanie zajmuje 0,05 s dla 999999999; Przerwałem test porównawczy dla 805306368 po 5 minutach ...f[805306368]
wraca{16384, 16384, 16384}
po 21 minutach. Użyłem {3} zamiast {4}. Próba rozwiązania go za pomocą sumy 4 niezerowych kwadratów zakończyła się niepowodzeniem po kilku godzinach pracy.IntegerPartitions[n,4,Range[Floor@Sqrt@n]^2
powinno również działać. Jednak nie sądzę, że powinieneś używać metody 1 do oceny, ponieważ nie jest ona zgodna z terminem określonym w pytaniu.Perl -
116 bajtów87 bajtów (patrz aktualizacja poniżej)Licząc shebang jako jeden bajt, dodano nowe znaki dla poziomego zdrowia psychicznego.
Coś z kombinacji kodu-kodu najszybszego kodu .
Średnia (najgorsza?) Złożoność przypadku wydaje się być
O (log n)O (n 0,07 ) . Nic, co znalazłem, działa wolniej niż 0,001s i sprawdziłem cały zakres od 900000000 do 999999999 . Jeśli znajdziesz coś, co trwa znacznie dłużej, około 0,1 s lub więcej, daj mi znać.Przykładowe użycie
Ostatnie dwa z nich wydają się najgorszym scenariuszem dla innych wniosków. W obu przypadkach pokazane rozwiązanie jest dosłownie pierwszą sprawdzoną rzeczą. Bo
123456789
to drugi.Jeśli chcesz przetestować zakres wartości, możesz użyć następującego skryptu:
Najlepiej gdy jest przesyłany do pliku. Zakres
1..1000000
zajmuje około 14 sekund na moim komputerze (71000 wartości na sekundę), a zakres999000000..1000000000
zajmuje około 20 sekund (50000 wartości na sekundę), zgodnie ze średnią złożonością O (log n) .Aktualizacja
Edycja : Okazuje się, że ten algorytm jest bardzo podobny do tego, który jest używany przez kalkulatory umysłowe od co najmniej stulecia .
Od czasu pierwotnego opublikowania sprawdziłem każdą wartość z zakresu od 1..1000000000 . Zachowanie „najgorszego przypadku” wykazało wartość 699731569 , która przetestowała w sumie 190 kombinacji przed znalezieniem rozwiązania. Jeśli uważasz 190 za małą stałą - a ja na pewno tak - najgorsze zachowanie w wymaganym zakresie można uznać za O (1) . Oznacza to, że tak szybko, jak wyszukiwanie rozwiązania z gigantycznego stołu, i średnio całkiem możliwe, że szybciej.
Ale inna sprawa. Po 190 iteracjach cokolwiek większego niż 144400 nawet nie przekroczyło pierwszego przejścia. Logika pierwszego przejścia jest bezwartościowa - nawet nie jest używana. Powyższy kod można nieco skrócić:
Który wykonuje tylko pierwszy przebieg wyszukiwania. Musimy jednak potwierdzić, że nie ma żadnych wartości poniżej 144400, które wymagałyby drugiego przejścia:
Krótko mówiąc, dla zakresu 1..1000000000 istnieje rozwiązanie o prawie stałym czasie i na to patrzysz.
Zaktualizowana aktualizacja
@Dennis i ja wprowadziliśmy kilka ulepszeń tego algorytmu. Możesz śledzić postępy w komentarzach poniżej i późniejszą dyskusję, jeśli Cię to interesuje. Średnia liczba iteracji dla wymaganego zakresu spadła z nieco ponad 4 do 1,229 , a czas potrzebny do przetestowania wszystkich wartości dla 1..1000000000 został skrócony z 18m 54s do 2m 41s. Najgorszy przypadek wymagał wcześniej 190 iteracji; najgorszy teraz, 854382778 , potrzebuje tylko 21 .
Ostateczny kod Pythona jest następujący:
Wykorzystuje dwie wstępnie obliczone tabele korekcji, jedna o wielkości 10 kb, druga 253 kb. Powyższy kod zawiera funkcje generatora dla tych tabel, chociaż prawdopodobnie powinny one zostać obliczone w czasie kompilacji.
Wersję ze skromniejszymi tabelami korekcji można znaleźć tutaj: http://codepad.org/1ebJC2OV Ta wersja wymaga średnio 1.620 iteracji na semestr, w najgorszym przypadku 38 , a cały zakres trwa około 3m 21s. Trochę czasu składa się na, za pomocą bitowego
and
dla b korekty, zamiast modulo.Ulepszenia
Nawet wartości parzyste częściej dają rozwiązanie niż nieparzyste.
Artykuł dotyczący obliczeń mentalnych powiązany z wcześniejszymi uwagami stwierdza, że jeśli po usunięciu wszystkich czterech czynników wartość do rozkładu jest parzysta, wartość tę można podzielić na dwa, a rozwiązanie odtworzyć:
Chociaż może to mieć sens w obliczeniach umysłowych (mniejsze wartości wydają się być łatwiejsze do obliczenia), nie ma to większego sensu algorytmicznego. Jeśli weźmiesz 256 losowych 4- kropek i zbadasz sumę kwadratów modulo 8 , przekonasz się, że wartości 1 , 3 , 5 i 7 są osiągane średnio 32 razy. Jednak wartości 2 i 6 są osiągane 48 razy. Pomnożenie nieparzystych wartości przez 2 znajdzie rozwiązanie średnio w 33% mniej iteracji. Rekonstrukcja jest następująca:
Należy zwrócić szczególną uwagę na to, i b mają taką samą parzystości oraz C i D , ale jeśli roztwór został znaleziony w wszystkim właściwej kolejności gwarantuje istnieć.
Niemożliwe ścieżki nie muszą być sprawdzane.
Po wybraniu drugiej wartości, b , rozwiązanie może już być niemożliwe, biorąc pod uwagę możliwe reszty kwadratowe dla dowolnego danego modułu. Zamiast sprawdzania i tak dalej lub przechodzenia do następnej iteracji, wartość b można „skorygować”, zmniejszając ją o najmniejszą wartość, która może doprowadzić do rozwiązania. Dwie tabele korekcji przechowują te wartości, jedną dla b , a drugą dla c . Użycie wyższego modułu (dokładniej, użycie modułu z relatywnie mniejszą liczbą reszt kwadratowych) spowoduje lepszą poprawę. Wartość a nie wymaga korekty; przez zmianę n na parzystą, wszystkie wartościa są ważne.
źródło
Python 3 (177)
Po zmniejszeniu nakładu,
N
aby nie był podzielny przez 4, musi on być wyrażony jako suma czterech kwadratów, przy czym jeden z nich ma albo największą możliwą wartość,a=int(N**0.5)
albo jeden mniejszą, pozostawiając jedynie niewielką pozostałą sumę dla trzech pozostałych kwadratów zaopiekować się. To znacznie zmniejsza przestrzeń wyszukiwania.Oto dowód później, że ten kod zawsze znajduje rozwiązanie. Chcemy znaleźć
a
tak, żen-a^2
jest to suma trzech kwadratów. Z Twierdzenia Trzech Kwadratów Legendre'a liczba jest sumą trzech kwadratów, chyba że jest to forma4^j(8*k+7)
. W szczególności takimi liczbami są 0 lub 3 (moduł 4).Pokazujemy, że żadne dwa kolejne nie
a
mogą sprawić, że pozostała ilośćN-a^2
ma taki kształt dla obu kolejnych wartości. Możemy to zrobić, po prostu tworząc tabelęa
iN
modulo 4, zauważając, żeN%4!=0
ponieważ wydobyliśmy wszystkie moce 4 zN
.Ponieważ nie ma dwóch kolejnych
a
podań(N-a*a)%4 in [0,3]
, jeden z nich jest bezpieczny w użyciu. Tak więc łapczywie używamy największej możliwejn
zn^2<=N
, in-1
. PonieważN<(n+1)^2
resztaN-a^2
reprezentowana jako suma trzech kwadratów jest co najwyżej(n+1)^2 -(n-1)^2
równa4*n
. Wystarczy więc sprawdzić tylko wartości do2*sqrt(n)
, czyli dokładnie tego zakresuR
.Można jeszcze bardziej zoptymalizować czas działania, zatrzymując się po pojedynczym rozwiązaniu, obliczając zamiast iterować ostatnią wartość
d
i wyszukując tylko wartościb<=c<=d
. Ale nawet bez tych optymalizacji najgorszy przypadek, jaki udało mi się skończyć w 45 sekund na moim komputerze.Łańcuch „for x in R” jest niefortunny. Prawdopodobnie można go skrócić przez podstawienie ciągu lub zastąpienie przez iterację pojedynczego indeksu, który koduje (a, b, c, d). Importowanie narzędzi itertools nie było tego warte.
Edycja: Zmieniono na
int(2*n**.5)+1
z,2*int(n**.5)+2
aby argument był czystszy, ta sama liczba znaków.źródło
5 => (2, 1, 0, 0)
5 => (2, 1, 0, 0)
uruchamiam na Ideone 3.2.3 lub w stanie bezczynności 3.2.2. Co dostajesz?5 => (2, 1, 0, 0)
. Czy w ogóle przeczytałeś komentarz? (Teraz mamy 3 komentarze z rzędu, które mają ten fragment kodu. Czy możemy kontynuować serię?)5 => (2, 1, 0, 0)
, to znaczy2^2 + 1^2 + 0^2 + 0^2 = 5
. Tak, możemy.5 => (2, 1, 0, 0)
jest poprawny. Przykłady w pytaniu uznają 0 ^ 2 = 0 za prawidłową liczbę kwadratową. Dlatego zinterpretowałem (jak mi się wydaje xnor), że British Color ma coś jeszcze. Brytyjska barwa, skoro nie odpowiedziałeś ponownie, czy możemy założyć, że tak naprawdę otrzymujesz2,1,0,0
?CJam ,
91907471 bajtówKompaktowy, ale wolniejszy niż moje inne podejście.
Wypróbuj online! Wklej kod , wpisz żądaną liczbę całkowitą w polu Input i kliknij Uruchom .
tło
Ten post rozpoczął się jako 99-bajtowa odpowiedź GolfScript . Podczas gdy wciąż było miejsce na ulepszenia, GolfScript nie ma wbudowanej funkcji sqrt. Zachowałem wersję GolfScript do wersji 5 , ponieważ była bardzo podobna do wersji CJam.
Jednak optymalizacje od wersji 6 wymagają operatorów, które nie są dostępne w GolfScript, więc zamiast publikować osobne objaśnienia dla obu języków, postanowiłem zrezygnować z mniej konkurencyjnej (i znacznie wolniejszej) wersji.
Zaimplementowany algorytm oblicza liczby za pomocą brutalnej siły:
W przypadku danych wejściowych
m
znajdźN
iW
takiem = 4**W * N
.Set
i = 257**2 * floor(sqrt(N/4))
.Set
i = i + 1
.Znajdź liczby całkowite,
j, k, l
takie jaki = 257**2 * j + 257 * k + l
, gdziek, l < 257
.Sprawdź, czy
d = N - j**2 - k**2 - l**2
jest to idealny kwadrat.Jeśli nie, i wróć do kroku 3.
Drukuj
2**W * j, 2**W * k, 2**W * l, 2**W * sqrt(m)
.Przykłady
Czasy odpowiadają procesorowi Intel Core i7-4700MQ.
Jak to działa
źródło
C 228
Jest to oparte na algorytmie na stronie Wikipedii, który jest brutalną siłą O (n).
źródło
GolfScript,
133130129 bajtówSzybki, ale długi. Nowa linia może zostać usunięta.
Wypróbuj online. Pamiętaj, że tłumacz online ma limit 5 sekund, więc może nie działać dla wszystkich numerów.
tło
Algorytm korzysta z twierdzenia trzech kwadratów Legendre'a , które stwierdza, że każda liczba naturalna n, która nie jest formą
można wyrazić jako sumę trzech kwadratów.
Algorytm wykonuje następujące czynności:
Wyraź liczbę jako
4**i * j
.Znajdź największą liczbę całkowitą
k
takie, żek**2 <= j
ij - k**2
spełnia hipotezę o trzech kwadratowy twierdzenie Legendre za.Set
i = 0
.Sprawdź, czy
j - k**2 - (i / 252)**2 - (i % 252)**2
jest to idealny kwadrat.Jeśli nie, zwiększ wartość
i
i wróć do kroku 4.Przykłady
Czasy odpowiadają procesorowi Intel Core i7-4700MQ.
Jak to działa
źródło
j-k-(i/252)-(i%252)
. Z twoich komentarzy (nie mogę właściwie odczytać kodu), wygląda na to, że masz na myślij-k-(i/252)^2-(i%252)^2
. BTW, odpowiednikj-k-(i/r)^2-(i%r)^2
gdzie r = sqrt (k) może zapisać kilka znaków (i wydaje się działać bez problemów, nawet dla k = 0 w moim programie C.)j-k^2-(i/252)^2-(i%252)^2
. Nadal czekam na OP, aby wyjaśnić, czy 0 jest prawidłowym wejściem, czy nie. Twój program daje1414 -nan 6 4.000000
dane wejściowe0
.0/
=> crash! : P Uruchomiłem twój rev 1 na moim laptopie (i7-4700MQ, 8 GiB RAM). Średnio czas wykonania wynosi 18,5 sekundy.Rev 1: C, 190
Jest to nawet bardziej wymagające pod względem pamięci niż rev 0. Ta sama zasada: zbuduj tabelę z wartością dodatnią dla wszystkich możliwych sum 2 kwadratów (i zero dla liczb, które nie są sumami dwóch kwadratów), a następnie przeszukaj ją.
W tej wersji użyj tablicy
short
zamiastchar
do przechowywania trafień, dzięki czemu mogę przechowywać pierwiastek jednego z dwóch kwadratów w tabeli zamiast samej flagi. Upraszcza top
znacznie funkcję (do dekodowania sumy 2 kwadratów), ponieważ nie ma potrzeby stosowania pętli.System Windows ma limit 2 GB na tablice. Mogę
short s[15<<26]
obejść ten, z którym jest tablica 1006632960 elementów, wystarczająca do spełnienia specyfikacji. Niestety, całkowita wielkość Runtime program jest nadal ponad 2 GB i (mimo szczypanie ustawienia OS) nie były w stanie przejechać ten rozmiar (choć jest to teoretycznie możliwe). Najlepsze, co mogę zrobić, toshort s[14<<26]
(939524096 elementy.)m*m
Musi być ściśle mniej niż to (30651 ^ 2 = 939483801.) Niemniej jednak program działa idealnie i powinien działać na każdym systemie operacyjnym, który nie ma tego ograniczenia.Nieskluczony kod
Rev 0 C, 219
To bestia głodna pamięci. Zajmuje tablicę 1 GB, oblicza wszystkie możliwe sumy 2 kwadratów i przechowuje flagę dla każdego w tablicy. Następnie dla wpisu z użytkownika, przeszukuje tablicę w poszukiwaniu dwóch sum 2 kwadratów a i za.
p
następnie funkcja ponownie przelicza oryginalne kwadraty, które zostały użyte do utworzenia sumy 2 kwadratówa
iz-a
drukuje je, pierwsza z każdej pary jako liczba całkowita, druga jako liczba podwójna (jeśli muszą to być wszystkie liczby całkowite, potrzebne są jeszcze dwa znaki,t
>m=t
.)Program zajmuje kilka minut, aby zbudować tabelę sum kwadratów (myślę, że jest to spowodowane problemami z zarządzaniem pamięcią, widzę, że przydzielanie pamięci rośnie powoli zamiast podskakiwać, jak można by się spodziewać.) Jednak po zakończeniu tej operacji generuje odpowiedzi bardzo szybko (jeśli kilka liczb ma być obliczonych, program od początku
scanf
można umieścić w pętli.nieprzypisany kod
Przykładowe dane wyjściowe
Pierwszy dotyczy pytania. Drugi został wybrany jako trudny do wyszukania. W takim przypadku program musi szukać aż do 8192 ^ 2 + 8192 ^ 2 = 134217728, ale zajmuje to tylko kilka sekund po zbudowaniu tabeli.
źródło
#include <stdio.h>
(dla scanf / printf) lub#include <math.h>
(dla sqrt.) kompilatora automatycznie łączy niezbędne biblioteki. Muszę za to podziękować Dennisowi (powiedział mi na to pytanie codegolf.stackexchange.com/a/26330/15599 ) Najlepsza wskazówka golfowa, jaką kiedykolwiek miałem.include
. Aby skompilować w systemie Linux, potrzebujesz flagi-lm
stdio
i kilka innych bibliotek, lecz niemath
nawet zinclude
? Rozumiem, że jeśli umieścisz flagę kompilatora, to i tak nie potrzebujeszinclude
? Cóż, to działa dla mnie, więc nie narzekam, jeszcze raz dziękuję za napiwek. BTW Mam nadzieję, że opublikuję zupełnie inną odpowiedź, korzystając z twierdzenia Legendre'a (ale nadal będzie używać asqrt
.)-lm
wpływa na linker, a nie na kompilator.gcc
decyduje się nie wymagać prototypów dla funkcji, które „zna”, więc działa z włącznikami lub bez nich. Pliki nagłówkowe zawierają jednak tylko prototypy funkcji, a nie same funkcje. W Linuksie (ale najwyraźniej nie w systemie Windows) libm biblioteki matematycznej nie jest częścią standardowych bibliotek, więc musisz poinstruować,ld
aby się z nim połączyć.Mathematica, 138 znakówOkazuje się więc, że daje to negatywne i urojone wyniki dla niektórych danych wejściowych, jak wskazał edc65 (np. 805306368), więc nie jest to prawidłowe rozwiązanie. Na razie zostawię to i może, jeśli naprawdę nie znoszę swojego czasu, wrócę i spróbuję to naprawić.
Lub niewykonane:
Nie patrzyłem zbyt mocno na algorytmy, ale spodziewam się, że to ten sam pomysł. Właśnie wymyśliłem oczywiste rozwiązanie i modyfikowałem je, aż zadziałało. Przetestowałem go dla wszystkich liczb od 1 do 1 miliarda i ... działa. Test trwa tylko około 100 sekund na moim komputerze.
Zaletą tego jest to, że ponieważ b, c i d są zdefiniowane z opóźnieniami,
:=
nie trzeba ich przedefiniowywać, gdy a jest zmniejszane. Dzięki temu zaoszczędziłem kilka dodatkowych linii, które miałem wcześniej. Mogę pograć w golfa dalej i zagnieździć zbędne części, ale oto pierwszy szkic.Aha, uruchamiasz go jak
S@123456789
i możesz go przetestować za pomocą{S@#, Total[(S@#)^2]} & @ 123456789
lub# == Total[(S@#)^2]&[123456789]
. Wyczerpujący test toWcześniej korzystałem z instrukcji Print [], ale bardzo ją to spowolniło, mimo że nigdy się nie wywołuje. Domyśl.
źródło
n - a^2 - b^2 - c^2
jako zmienną i sprawdzić, czyd^2
jest równa.a * 4^(2^k)
nak>=2
, po ekstrakcji wszystkie moce 4, tak żea
nie jest wielokrotnością 4 (ale może być jeszcze). Co więcej, każdya
ma albo 3 mod 4, albo dwukrotność takiej liczby. Najmniejszy to 192.Haskell 123 + 3 = 126
Prosta brutalna siła ponad wstępnie obliczonymi kwadratami.
Potrzebuje
-O
opcji kompilacji (do tego dodałem 3 znaki). W najgorszym przypadku 999950883 zajmuje mniej niż 1 minutę.Testowane tylko na GHC.
źródło
C: 198 znakówPrawdopodobnie uda mi się to zmniejszyć do nieco ponad 100 znaków. To, co lubię w tym rozwiązaniu, to minimalna ilość śmieci, tylko zwykła pętla for-for, robiąca to, co powinna zrobić pętla for (co ma być szalone).
I mocno upiększony:
Edycja: Nie jest wystarczająco szybki dla wszystkich danych wejściowych, ale wrócę z innym rozwiązaniem. Pozwolę, by ten potrójny bałagan operacji został na razie.
źródło
Rev B: C, 179
Dzięki @Dennis za ulepszenia. Pozostała część odpowiedzi poniżej nie jest aktualizowana od wersji A.
Rev A: C, 195
Znacznie szybciej niż moja inna odpowiedź i przy znacznie mniejszej pamięci!
Wykorzystuje to http://en.wikipedia.org/wiki/Legendre%27s_three-square_theorem . Dowolną liczbę nie w poniższej formie można wyrazić jako sumę 3 kwadratów (nazywam to formą zabronioną):
4^a*(8b+7), or equivalently 4^a*(8b-1)
Zauważ, że wszystkie nieparzyste liczby kwadratowe mają postać,
(8b+1)
a wszystkie parzyste liczby kwadratowe są powierzchownie formą4b
. Ukrywa to jednak fakt, że wszystkie parzyste liczby kwadratowe mają formę4^a*(odd square)==4^a*(8b+1)
. W rezultacie2^x-(any square number < 2^(x-1))
nieparzystex
zawsze będą miały zabronioną formę. Dlatego te liczby i ich wielokrotności są trudnymi przypadkami, dlatego tak wiele programów tutaj dzieli siły 4 jako pierwszy krok.Jak stwierdzono w odpowiedzi @ xnor,
N-a*a
nie może mieć formy zabronionej dla 2 kolejnych wartościa
. Poniżej przedstawiam uproszczoną formę jego stołu. Oprócz faktu, że po podzieleniu przez 4N%4
nie może być równe 0, należy pamiętać, że istnieją tylko 2 możliwe wartości dla(a*a)%4
.Chcemy więc unikać wartości,
(N-a*a)
które mogą mieć niedozwoloną formę, a mianowicie te, w których(N-a*a)%4
jest 3 lub 0. Jak widać, nie może się tak zdarzyćN
zarówno z nieparzystymi, jak i parzystymi(a*a)
.Mój algorytm działa w ten sposób:
Szczególnie podoba mi się sposób, w jaki robię krok 3. Dodam 3 do
N
, więc zmniejszenie jest wymagane, jeśli(3+N-a*a)%4 =
3 lub 2. (ale nie 1 lub 0.) Podziel to przez 2, a całą pracę można wykonać za pomocą dość prostego wyrażenia .Nieskluczony kod
Zwróć uwagę na pojedynczą
for
pętlęq
i użycie dzielenia / modulo, aby uzyskać z niej wartościb
ic
. Próbowałem użyća
jako dzielnika zamiast 256, aby zapisać bajty, ale czasami wartośća
nie była właściwa i program zawiesił się, być może na czas nieokreślony. 256 był najlepszym kompromisem, jaki mogę zastosować>>8
zamiast/256
do podziału.Wynik
Ciekawym dziwactwem jest to, że jeśli wprowadzisz liczbę kwadratową,
N-(a*a)
= 0. Ale program wykrywa, że0%4
= 0 i zmniejsza do następnego kwadratu w dół. W rezultacie liczby kwadratowe są zawsze rozkładane na grupę mniejszych kwadratów, chyba że mają formę4^x
.źródło
m=1
wcześniejmain
. 2. Wykonajscanf
wfor
instrukcji. 3. Użyjfloat
zamiastdouble
. 4.n%4<1
jest krótszy niż!(n%4)
. 5. W ciągu formatu printf jest przestarzała przestrzeń.n-=a*a
nie działa, ponieważa
można go później zmodyfikować (daje błędne odpowiedzi i zawiesza się na małej liczbie przypadków, np. 100 + 7 = 107). Uwzględniłem całą resztę. Przydałoby się coś skrócić,printf
ale myślę, że jedyną odpowiedzią jest zmiana języka. Kluczem do prędkości jest szybkie ustalenie dobrej wartościa
. Napisany w C i o powierzchni wyszukiwania mniejszej niż 256 ^ 2, jest to prawdopodobnie najszybszy program tutaj.printf
instrukcji wydaje się trudne bez użycia makra lub tablicy, co zwiększyłoby masę w innym miejscu. Zmiana języków wydaje się „łatwa”. Twoje podejście ważyłoby 82 bajty w CJam.JavaScript -
175 191 176173 znakówBrutalna siła, ale szybka.
Edycja Szybka, ale niewystarczająca do niektórych nieprzyjemnych danych wejściowych. Musiałem dodać pierwszy krok redukcji przez wielokrotność 4.
Edycja 2 Pozbądź się funkcji, wyjdź wewnątrz pętli, a następnie wymuś rywalizację o wyjście
Edytuj 3 0 niepoprawne dane wejściowe
Nie golfowany:
Przykładowe dane wyjściowe
źródło