Lagranges Vierquadrat-Theorem besagt, dass jede natürliche Zahl als die Summe von vier Quadratzahlen dargestellt werden kann. Ihre Aufgabe ist es, ein Programm zu schreiben, das dies tut.
Input: Eine natürliche Zahl (unter 1 Milliarde)
Ausgabe: Vier Zahlen, deren Quadrate sich zu dieser Zahl addieren (Reihenfolge spielt keine Rolle)
Hinweis: Sie müssen keine Brute-Force-Suche durchführen! Details hier und hier . Wenn es eine Funktion gibt, die dieses Problem trivialisiert (ich werde feststellen), ist dies nicht zulässig. Automatisierte Primfunktionen und Quadratwurzel sind erlaubt. Wenn es mehr als eine Darstellung gibt, ist jede in Ordnung. Wenn Sie sich für brachiale Gewalt entscheiden, muss sie innerhalb einer angemessenen Zeit (3 Minuten) ausgeführt werden.
Probeneingabe
123456789
Beispielausgabe (entweder ist in Ordnung)
10601 3328 2 0
10601 3328 2
Antworten:
CJam, 50 Bytes
Meine dritte (und letzte, ich verspreche es) Antwort. Dieser Ansatz basiert stark auf der Antwort von primo .
Probieren Sie es online im CJam-Interpreter aus .
Verwendung
Hintergrund
Primo aktualisierten Algorithmus Nachdem ich, ich hatte , um zu sehen , wie ein CJam Implementierung würde punkten:
Nur 58 Bytes! Dieser Algorithmus wird in nahezu konstanter Zeit ausgeführt und weist für verschiedene Werte von keine großen Schwankungen auf
N
. Lassen Sie uns das ändern ...Anstatt bei zu beginnen
floor(sqrt(N))
und zu dekrementieren, können wir bei zu beginnen1
und zu inkrementieren. Das spart 4 Bytes.Anstatt
N
als4**a * b
auszudrücken, können wir es alsp**(2a) * b
- wop
ist der kleinste Primfaktor vonN
- ausdrücken , um 1 weiteres Byte zu sparen.Die vorherige Änderung ermöglicht es uns , die Umsetzung etwas zu ändern (ohne den Algorithmus selbst zu berühren): Anstelle der Unterteilung
N
durchp**(2a)
und multiplizieren Sie die Lösung durchp**a
, können wir direkt die möglichen Lösungen auf ein Vielfaches von beschränkenp**a
. Dies spart 2 weitere Bytes.Wenn Sie die erste Ganzzahl nicht auf ein Vielfaches von beschränken, wird
p**a
ein zusätzliches Byte gespeichert.Endgültiger Algorithmus
Finde
a
undb
so, dassN = p**(2a) * b
, wob
nicht ein Vielfaches vonp**2
undp
der kleinste Primfaktor von istN
.Einstellen
j = p**a
.Einstellen
k = floor(sqrt(N - j**2) / A) * A
.Einstellen
l = floor(sqrt(N - j**2 - k**2) / A) * A
.Einstellen
m = floor(sqrt(N - j**2 - k**2 - l**2) / A) * A
.Wenn ja
N - j**2 - k**2 - l**2 - m**2 > 0
, stellen Sie einj = j + 1
und fahren Sie mit Schritt 3 fort.Dies kann wie folgt implementiert werden:
Benchmarks
Ich habe auf meinem Intel Core i7-3770 alle 5 Versionen über alle positiven ganzen Zahlen bis zu 999.999.999 ausgeführt, die Ausführungszeit gemessen und die für die Lösungsfindung erforderlichen Iterationen gezählt.
Die folgende Tabelle zeigt die durchschnittliche Anzahl der Iterationen und die Ausführungszeit für eine einzelne Ganzzahl:
Mit nur 4 Iterationen und 6,6 Mikrosekunden pro Ganzzahl ist der Algorithmus von primo unglaublich schnell.
Ab
floor(sqrt(N))
ist sinnvoller, da wir dann für die Summe der verbleibenden drei Quadrate kleinere Werte haben. Wie erwartet ist der Start bei 1 viel langsamer.Dies ist ein klassisches Beispiel für eine schlecht umgesetzte gute Idee. Um die Codegröße tatsächlich zu reduzieren , verlassen wir uns darauf
mF
, dass die ganze Zahl faktorisiert wirdN
. Obwohl Version 3 weniger Iterationen erfordert als Version 2, ist es in der Praxis viel langsamer.Obwohl sich der Algorithmus nicht ändert, ist Version 4 viel langsamer. Dies liegt daran, dass in jeder Iteration eine zusätzliche Gleitkommadivision und eine ganzzahlige Multiplikation durchgeführt wird.
Für die Eingabe benötigt der
N = p**(2a) ** b
Algorithmus 5(k - 1) * p**a + 1
Iterationen, wobeik
die Anzahl der Iterationen ist, die der Algorithmus 4 benötigt. Wennk = 1
odera = 0
, macht dies keinen Unterschied.Jede Eingabe des Formulars
4**a * (4**c * (8 * d + 7) + 1)
kann jedoch sehr schlecht ausgeführt werden. Für den Startwertj = p**a
,N - 4**a = 4**(a + c) * (8 * d + 7)
so kann es nicht als eine Summe von drei Quadraten ausgedrückt werden. Somit sindk > 1
zumindestp**a
Iterationen erforderlich.Zum Glück ist der ursprüngliche Algorithmus von primo unglaublich schnell und
N < 1,000,000,000
. Der schlechteste Fall, den ich von Hand finden konnte265,289,728 = 4**10 * (4**1 * (7 * 8 + 7) + 1)
, erfordert 6.145 Iterationen. Die Ausführungszeit auf meinem Computer beträgt weniger als 300 ms. Im Durchschnitt ist diese Version 13,5-mal langsamer als die Implementierung des primo-Algorithmus.quelle
N
als4**a * b
auszudrücken, können wir es als ausdrückenp**(2a) * b
." Dies ist eigentlich eine Verbesserung . Ich hätte das gerne aufgenommen, aber es war viel länger (das Ideal ist, den größten perfekten Quadratfaktor zu finden). Msgstr "Mit 1 zu beginnen und zu erhöhen, spart 4 Bytes." Das ist definitiv langsamer. Die Laufzeit für einen bestimmten Bereich ist 4-5 mal so lang. "Alle positiven ganzen Zahlen bis 999.999.999 haben 24,67 Stunden gedauert, was eine durchschnittliche Ausführungszeit von 0,0888 Millisekunden pro ganze Zahl ergibt." Perl nahm nur 2,5 Stunden , um den gesamten Bereich knirschen, und die Python - Übersetzung ist 10x schneller;)p**a
ist eine Verbesserung, aber es ist eine kleine Verbesserung. Die Division durch den größten perfekten Quadratfaktor macht einen großen Unterschied, wenn man mit 1 beginnt. Es ist immer noch eine Verbesserung, wenn man vom ganzzahligen Teil der Quadratwurzel ausgeht. Die Implementierung würde nur zwei weitere Bytes kosten. Die miserable Ausführungszeit scheint auf meine Verbesserungen zurückzuführen zu sein, nicht auf CJam. Ich werde die Tests für alle Algorithmen (einschließlich der von Ihnen vorgeschlagenen) wiederholen und dabei die Iterationen zählen, anstatt die Wandzeit zu messen. Mal sehen, wie lange das dauert ...1\
tauscht sie mit 1 (Akkumulator) aus,mF
schiebt ihre Faktorisierung vor und{~2/#*}/
erhöht jeden Primfaktor auf seinen Exponenten, geteilt durch zwei, und multipliziert ihn dann mit dem Akkumulator. Für die direkte Implementierung Ihres Algorithmus werden nur 2 Bytes hinzugefügt. Der kleine Unterschied ist hauptsächlich auf die unangenehme Art zurückzuführen, wie ich den Exponenten von 4 finden musste, da CJam keine while- Schleife hat (anscheinend) ...FRACTRAN:
15698 FraktionenDa dies ein klassisches Problem der Zahlentheorie ist, gibt es keinen besseren Weg, dies zu lösen, als Zahlen zu verwenden!
Nimmt die Eingabe der Form 2 n × 193 auf und gibt 3 a × 5 b × 7 c × 11 d aus . Könnte in 3 Minuten laufen, wenn Sie einen wirklich guten Dolmetscher haben. Vielleicht.
Hinweis
Der Code entspricht dem folgenden Pseudo-Python:
quelle
Mathematica
61 6651Es werden drei Methoden gezeigt. Nur der erste Ansatz erfüllt den Zeitbedarf.
1-FindInstance (51 Zeichen)
Dies gibt eine einzelne Lösung der Gleichung zurück.
Beispiele und Timings
2-IntegerPartitions
Dies funktioniert auch, ist jedoch zu langsam, um die Geschwindigkeitsanforderungen zu erfüllen.
Range[0, Floor@Sqrt@n]^2
ist die Menge aller Quadrate, die kleiner ist als die Quadratwurzel vonn
(das größtmögliche Quadrat in der Partition).{4}
erfordert, dass die ganzzahligen Partitionenn
aus 4 Elementen aus der oben genannten Menge von Quadraten bestehen.1
, innerhalb der FunktionIntegerPartitions
gibt die erste Lösung zurück.[[1]]
entfernt die äußeren Klammern; Die Lösung wurde als Satz von einem Element zurückgegeben.3-PowerRepresentations
PowerRepresentations gibt alle Lösungen für das 4-Quadrate-Problem zurück. Es kann auch nach Summen anderer Mächte aufgelöst werden.
PowersRepresentations gibt in weniger als 5 Sekunden die 181 Möglichkeiten zurück, um 123456789 als die Summe von 4 Quadraten auszudrücken:
Für andere Beträge ist es jedoch viel zu langsam.
quelle
IntegerPartitions
. Wie Sie an den Timings erkennen können, variiert die Geschwindigkeit stark, je nachdem, ob die erste (größte) Zahl nahe an der Quadratwurzel von liegtn
. Vielen Dank, dass Sie den Spezifikationsverstoß in der früheren Version abgefangen haben.f[805306368]
? Ohne zuerst durch Potenzen von 4 zu teilen, dauert meine Lösung 0,05 s für 999999999; Ich habe den Benchmark für 805306368 nach 5 Minuten abgebrochen ...f[805306368]
kehrt{16384, 16384, 16384}
nach 21 Minuten zurück. Ich habe {3} anstelle von {4} verwendet. Der Versuch, es mit einer Summe von 4 Quadraten ungleich Null zu lösen, schlug nach mehreren Stunden Laufen fehl.IntegerPartitions[n,4,Range[Floor@Sqrt@n]^2
sollte auch funktionieren. Ich denke jedoch nicht, dass Sie Methode 1 für Ihre Punktzahl verwenden sollten, da sie nicht dem in der Frage angegebenen Zeitlimit entspricht.Perl -
116 Bytes87 Bytes (siehe Update unten)Zählt man den Shebang als ein Byte, werden Zeilenumbrüche hinzugefügt, um die horizontale Vernunft zu gewährleisten.
So etwas wie eine Kombination aus Code-Golf -Code- Einreichung mit dem schnellsten Code .
Die durchschnittliche (schlechteste?) Fallkomplexität scheint
O (log n)O (n 0,07 ) zu sein . Nichts, was ich gefunden habe, läuft langsamer als 0,001s und ich habe den gesamten Bereich von 900000000 - 999999999 überprüft . Wenn Sie etwas finden, das wesentlich länger dauert (~ 0,1s oder länger), lassen Sie es mich bitte wissen.Beispielnutzung
Die letzten beiden scheinen Worst-Case-Szenarien für andere Einreichungen zu sein. In beiden Fällen wird die gezeigte Lösung buchstäblich als erstes überprüft. Für
123456789
ist es das zweite.Wenn Sie einen Wertebereich testen möchten, können Sie das folgende Skript verwenden:
Am besten in eine Datei umleiten.
1..1000000
Auf meinem Computer dauert der Bereich ungefähr 14 Sekunden (71000 Werte pro Sekunde), und der Bereich999000000..1000000000
dauert ungefähr 20 Sekunden (50000 Werte pro Sekunde), was der durchschnittlichen Komplexität von O (log n) entspricht .Aktualisieren
Bearbeiten : Es stellt sich heraus, dass dieser Algorithmus einem sehr ähnlich ist, der von mentalen Taschenrechnern seit mindestens einem Jahrhundert verwendet wird .
Seit dem ursprünglichen Posten habe ich jeden Wert im Bereich von 1..1000000000 überprüft . Das "Worst-Case" -Verhalten wurde durch den Wert 699731569 angezeigt , bei dem insgesamt 190 Kombinationen getestet wurden, bevor eine Lösung gefunden wurde. Wenn Sie 190 als kleine Konstante betrachten - und das tue ich sicherlich -, kann das Worst-Case-Verhalten im erforderlichen Bereich als O (1) betrachtet werden . Das ist so schnell wie das Nachschlagen der Lösung von einem riesigen Tisch und im Durchschnitt möglicherweise sogar schneller.
Eine andere Sache. Nach 190 Iterationen hat alles, was größer als 144400 ist, noch nicht einmal den ersten Durchgang geschafft. Die Logik für die erste Durchquerung ist wertlos - sie wird nicht einmal verwendet. Der obige Code kann einiges gekürzt werden:
Welches führt nur den ersten Durchgang der Suche. Wir müssen jedoch bestätigen, dass es keine Werte unter 144400 gibt , für die der zweite Durchgang erforderlich ist:
Kurz gesagt, für den Bereich 1..1000000000 gibt es eine nahezu konstante Zeitlösung , die Sie sich ansehen .
Aktualisiertes Update
@Dennis und ich haben diesen Algorithmus mehrfach verbessert. Sie können den Fortschritt in den Kommentaren und der anschließenden Diskussion verfolgen, wenn dies Sie interessiert. Die durchschnittliche Anzahl der Iterationen für den erforderlichen Bereich ist von etwas mehr als 4 auf 1,229 gesunken , und die Zeit, die zum Testen aller Werte für 1..1000000000 erforderlich ist, wurde von 18 auf 2 Millionen Sekunden auf 41 Sekunden gesenkt . Der schlimmste Fall erforderte zuvor 190 Iterationen. Der schlimmste Fall, 854382778 , benötigt nur noch 21 .
Der endgültige Python-Code lautet wie folgt:
Dabei werden zwei vorberechnete Korrekturtabellen verwendet, eine mit einer Größe von 10 KB und eine mit einer Größe von 253 KB. Der obige Code enthält die Generatorfunktionen für diese Tabellen, obwohl diese wahrscheinlich zur Kompilierungszeit berechnet werden sollten.
Eine Version mit kleineren Korrekturtabellen finden Sie hier: http://codepad.org/1ebJC2OV Diese Version erfordert durchschnittlich 1.620 Iterationen pro Term, wobei der schlechteste Fall 38 ist. Die gesamte Reichweite beträgt ca. 3 m 21 s. Ein wenig Zeit wird wettgemacht, indem
and
für die b- Korrektur bitweise anstelle von Modulo verwendet wird.Verbesserungen
Gerade Werte ergeben eher eine Lösung als ungerade Werte.
Der Artikel zur mentalen Berechnung, der mit dem vorherigen Artikel verknüpft ist, stellt fest, dass, wenn nach dem Entfernen aller vier Faktoren der zu zerlegende Wert gerade ist, dieser Wert durch zwei geteilt und die Lösung rekonstruiert werden kann:
Obwohl dies für die mentale Berechnung sinnvoll sein kann (kleinere Werte sind in der Regel einfacher zu berechnen), ist es algorithmisch wenig sinnvoll. Wenn Sie 256 zufällige 4- Tupel nehmen und die Summe der Quadrate Modulo 8 untersuchen , werden Sie feststellen, dass die Werte 1 , 3 , 5 und 7 jeweils durchschnittlich 32- mal erreicht werden. Die Werte 2 und 6 werden jedoch jeweils 48 Mal erreicht. Wenn Sie ungerade Werte mit 2 multiplizieren , erhalten Sie im Durchschnitt eine Lösung mit 33% weniger Iterationen. Die Rekonstruktion ist die folgende:
Es muss darauf geachtet werden, dass a und b sowie c und d die gleiche Parität haben , aber wenn überhaupt eine Lösung gefunden wurde, ist eine ordnungsgemäße Reihenfolge garantiert.
Unmögliche Pfade müssen nicht überprüft werden.
Nach der Auswahl des zweiten Wertes, b , kann es bereits unmöglich sein, dass eine Lösung existiert, wenn die möglichen quadratischen Reste für ein gegebenes Modulo gegeben sind. Anstatt es trotzdem zu überprüfen oder mit der nächsten Iteration fortzufahren, kann der Wert von b "korrigiert" werden, indem er um den kleinsten Betrag verringert wird, der möglicherweise zu einer Lösung führen könnte. In den beiden Korrekturtabellen werden diese Werte gespeichert, einer für b und der andere für c . Die Verwendung eines höheren Modulos (genauer gesagt eines Moduls mit relativ weniger quadratischen Resten) führt zu einer besseren Verbesserung. Der Wert a muss nicht korrigiert werden. durch Ändern von n auf gerade werden alle Werte vona sind gültig.
quelle
Python 3 (177)
Nachdem wir die Eingabe so reduziert haben
N
, dass sie nicht durch 4 teilbar ist, muss sie als Summe von vier Quadraten ausgedrückt werden, wobei eines der Quadrate entweder der größtmögliche Werta=int(N**0.5)
oder ein kleinerer Wert ist , sodass nur ein kleiner Rest für die Summe der drei anderen Quadrate übrig bleibt sich kümmern um. Dies reduziert den Suchraum erheblich.Hier ist ein Beweis später findet dieser Code immer eine Lösung. Wir möchten ein
a
so finden,n-a^2
das die Summe von drei Quadraten ist. Nach dem Drei-Quadrate-Satz von Legendre ist eine Zahl die Summe von drei Quadraten, sofern es sich nicht um die Form handelt4^j(8*k+7)
. Insbesondere sind solche Zahlen entweder 0 oder 3 (Modulo 4).Wir zeigen, dass keine zwei aufeinanderfolgenden
a
den RestbetragN-a^2
für beide aufeinanderfolgenden Werte eine solche Form haben können. Wir können dies tun, indem wir einfach eine Tabelle vona
undN
modulo 4 erstellen, wobeiN%4!=0
wir beachten, dass wir alle Potenzen von 4 aus extrahiert habenN
.Da keine zwei aufeinanderfolgenden
a
geben(N-a*a)%4 in [0,3]
, ist einer von ihnen sicher zu bedienen. Also nutzen wir gierig das größtmöglichen
mitn^2<=N
undn-1
. DaN<(n+1)^2
ist der RestN-a^2
, der als Summe von drei Quadraten dargestellt werden soll, höchstens(n+1)^2 -(n-1)^2
gleich4*n
. Es genügt also, nur Werte bis zu überprüfen2*sqrt(n)
, die genau dem Bereich entsprechenR
.Man könnte die Laufzeit weiter optimieren, indem man nach einer einzelnen Lösung stoppt, anstatt nach dem letzten Wert zu iterieren
d
und nur zwischen den Werten zu suchenb<=c<=d
. Aber selbst ohne diese Optimierungen war die schlimmste Instanz, die ich finden konnte, in 45 Sekunden auf meinem Computer beendet.Die Kette von "für x in R" ist unglücklich. Sie kann möglicherweise durch Zeichenfolgensubstitution oder durch Iteration über einen einzelnen Index, der (a, b, c, d) codiert, verkürzt werden. Der Import von itertools hat sich nicht gelohnt.
Bearbeiten: Geändert in
int(2*n**.5)+1
von2*int(n**.5)+2
, um das Argument sauberer zu machen, bei gleicher Zeichenanzahl.quelle
5 => (2, 1, 0, 0)
5 => (2, 1, 0, 0)
starte auf Ideone 3.2.3 oder Idle 3.2.2. Was bekommst du?5 => (2, 1, 0, 0)
. Hast du den Kommentar überhaupt gelesen? (Jetzt haben wir 3 Kommentare in einer Reihe, die dieses Code-Snippet enthalten. Können wir die Serie am Laufen halten?)5 => (2, 1, 0, 0)
, heißt das2^2 + 1^2 + 0^2 + 0^2 = 5
. Also ja, wir können.5 => (2, 1, 0, 0)
ist er richtig. In den Beispielen in der Frage wird 0 ^ 2 = 0 als gültige Quadratzahl betrachtet. Deshalb habe ich (wie ich glaube, xnor) interpretiert, dass British Color etwas anderes hat. Britische Farbe, wie Sie nicht wieder geantwortet haben, können wir davon ausgehen, dass Sie tatsächlich bekommen2,1,0,0
?CJam ,
91907471 BytesKompakt, aber langsamer als mein anderer Ansatz.
Probieren Sie es online! Fügen Sie den Code ein , geben Sie die gewünschte Ganzzahl in die Eingabe ein und klicken Sie auf Ausführen .
Hintergrund
Dieser Beitrag begann als 99-Byte-GolfScript-Antwort . Während es noch Verbesserungspotential gab, fehlt GolfScript die eingebaute sqrt-Funktion. Ich habe die GolfScript-Version bis beibehalten 5 , da sie der CJam-Version sehr ähnlich war.
Die Optimierungen seit Revision 6 erfordern jedoch Operatoren, die in GolfScript nicht verfügbar sind. Daher habe ich mich entschlossen, die weniger wettbewerbsfähige (und viel langsamere) Version zu streichen, anstatt separate Erklärungen für beide Sprachen zu veröffentlichen.
Der implementierte Algorithmus berechnet die Zahlen mit Brute Force:
Für die Eingabe
m
findenN
undW
som = 4**W * N
.Einstellen
i = 257**2 * floor(sqrt(N/4))
.Einstellen
i = i + 1
.Finden Sie ganze Zahlen,
j, k, l
so dassi = 257**2 * j + 257 * k + l
, wok, l < 257
.Überprüfen Sie, ob
d = N - j**2 - k**2 - l**2
es sich um ein perfektes Quadrat handelt.Wenn nicht, fahren Sie mit Schritt 3 fort.
Drucken
2**W * j, 2**W * k, 2**W * l, 2**W * sqrt(m)
.Beispiele
Die Timings entsprechen einem Intel Core i7-4700MQ.
Wie es funktioniert
quelle
C 228
Dies basiert auf dem Algorithmus auf der Wikipedia-Seite, bei dem es sich um eine O (n) Brute Force handelt.
quelle
GolfScript,
133130129 BytesSchnell, aber langwierig. Der Zeilenumbruch kann entfernt werden.
Probieren Sie es online aus. Beachten Sie, dass der Online-Dolmetscher ein Zeitlimit von 5 Sekunden hat und daher möglicherweise nicht für alle Nummern funktioniert.
Hintergrund
Der Algorithmus nutzt den Drei-Quadrate-Satz von Legendre , der besagt, dass jede natürliche Zahl n nicht von der Form ist
kann als die Summe von drei Quadraten ausgedrückt werden .
Der Algorithmus führt Folgendes aus:
Drücken Sie die Nummer als aus
4**i * j
.Finden Sie die größte Ganzzahl
k
so, dassk**2 <= j
undj - k**2
die Hypothese von Legendres Drei-Quadrat-Theorem erfüllt.Einstellen
i = 0
.Überprüfen Sie, ob
j - k**2 - (i / 252)**2 - (i % 252)**2
es sich um ein perfektes Quadrat handelt.Ist dies nicht der Fall, erhöhen Sie den Wert
i
und fahren Sie mit Schritt 4 fort.Beispiele
Die Timings entsprechen einem Intel Core i7-4700MQ.
Wie es funktioniert
quelle
j-k-(i/252)-(i%252)
. Aus Ihren Kommentaren (ich kann den Code eigentlich nicht lesen) geht hervor, dass Sie meinenj-k-(i/252)^2-(i%252)^2
. Übrigens kann das Äquivalent vonj-k-(i/r)^2-(i%r)^2
wobei r = sqrt (k) einige Zeichen speichern (und scheint auch für k = 0 in meinem C-Programm ohne Probleme zu funktionieren.)j-k^2-(i/252)^2-(i%252)^2
. Ich warte immer noch darauf, dass das OP klärt, ob 0 eine gültige Eingabe ist oder nicht. Ihr Programm gibt1414 -nan 6 4.000000
zur Eingabe0
.0/
=> Absturz! : P Ich habe Ihre Version 1 auf meinem Laptop ausgeführt (i7-4700MQ, 8 GiB RAM). Die Ausführungszeit beträgt durchschnittlich 18,5 Sekunden.Rev. 1: C, 190
Dies ist noch speicherhungriger als Rev. 0. Gleiches Prinzip: Erstellen Sie eine Tabelle mit einem positiven Wert für alle möglichen Summen von 2 Quadraten (und Null für die Zahlen, die keine Summen von zwei Quadraten sind), und durchsuchen Sie sie dann.
Verwenden Sie in dieser Version ein Array von
short
anstelle vonchar
, um die Treffer zu speichern, damit ich die Wurzel eines der beiden Quadrate in der Tabelle anstelle eines Flags speichern kann. Dies vereinfacht die Funktionp
(zum Decodieren der Summe von 2 Quadraten) erheblich, da keine Schleife erforderlich ist.Windows hat eine Beschränkung von 2 GB für Arrays. Ich kann
short s[15<<26]
das umgehen, womit sich ein Array von 1006632960 Elementen ergibt, das ausreicht, um der Spezifikation zu entsprechen. Leider beträgt die Gesamtgröße der Programmlaufzeit immer noch mehr als 2 GB, und ich konnte diese Größe (obwohl dies theoretisch möglich ist)short s[14<<26]
(939524096 Elemente) trotz Optimierung der Betriebssystemeinstellungen nicht überschreiten.m*m
Muß Streng weniger (30651 ^ 2 = 939483801.) Trotzdem läuft das Programm einwandfrei und sollte auf jedem Betriebssystem ohne diese Einschränkung funktionieren.Ungolfed Code
Rev. 0 C, 219
Dies ist eine erinnerungshungrige Bestie. Es benötigt ein 1-GB-Array, berechnet alle möglichen Summen von 2 Quadraten und speichert für jedes ein Flag im Array. Dann durchsucht es für die Benutzereingabe z das Array nach zwei Summen von 2 Quadraten a und za.
Die Funktion setzt
p
dann die ursprünglichen Quadrate, aus denen die Summe von 2 Quadraten gebildet wurde, erneut zusammena
und gibtz-a
sie aus, wobei das erste jedes Paares als Ganzzahl und das zweite als Doppelzahl (wenn es sich um ganze Zahlen handeln muss , werden zwei weitere Zeichen benötigt).t
>m=t
.)Das Programm benötigt ein paar Minuten, um die Tabelle der Quadratsummen zu erstellen (ich glaube, dies liegt an Problemen mit der Speicherverwaltung. Ich sehe, dass die Speicherzuweisung langsam ansteigt, anstatt wie erwartet zu springen.) Sobald dies erledigt ist Es liefert sehr schnell Antworten (wenn mehrere Zahlen berechnet werden sollen, kann das Programm von nun
scanf
an in eine Schleife gestellt werden.ungolfed code
Beispielausgabe
Das erste ist pro die Frage. Der zweite war schwer zu finden. In diesem Fall muss das Programm bis zu 8192 ^ 2 + 8192 ^ 2 = 134217728 suchen, dauert jedoch nach dem Erstellen der Tabelle nur einige Sekunden.
quelle
#include <stdio.h>
(für scanf / printf) noch#include <math.h>
(für sqrt.) den Compiler einfügen muss verknüpft die benötigten Bibliotheken automatisch. Ich muss mich bei Dennis dafür bedanken (er erzählte mir auf dieser Frage codegolf.stackexchange.com/a/26330/15599 ). Bester Golftipp, den ich je hatte.include
. Zum Kompilieren unter Linux benötigen Sie das Flag-lm
stdio
und mehrere andere Bibliotheken, aber nichtmath
einmal mit deminclude
? Womit verstehe ich, wenn Sie das Compiler-Flag setzen, brauchen Sie dasinclude
trotzdem nicht? Nun, es funktioniert für mich, also beschwere ich mich nicht, nochmals vielen Dank für den Tipp. Übrigens hoffe ich, eine ganz andere Antwort zu veröffentlichen, indem ich den Satz von Legendre nutze (aber es wird immer noch einsqrt
. Verwenden )-lm
Betrifft den Linker, nicht den Compiler.gcc
Es werden keine Prototypen für Funktionen benötigt, die es "kennt", sodass es mit oder ohne Includes funktioniert. Die Header-Dateien stellen jedoch nur Funktionsprototypen zur Verfügung, nicht die Funktionen selbst. Unter Linux (aber anscheinend nicht unter Windows) ist die Mathematikbibliothek libm nicht Teil der Standardbibliotheken. Sie müssen also anweisenld
, eine Verknüpfung zu dieser Bibliothek herzustellen .Mathematica, 138 ZeichenEs stellt sich also heraus, dass dies für bestimmte Eingaben negative und imaginäre Ergebnisse liefert, wie von edc65 (z. B. 805306368) hervorgehoben. Dies ist also keine gültige Lösung. Ich lasse es fürs Erste und wenn ich meine Zeit wirklich hasse, gehe ich zurück und versuche es zu reparieren.
Oder unsquished:
Ich habe mir die Algorithmen nicht so genau angesehen, aber ich gehe davon aus, dass dies die gleiche Idee ist. Ich habe gerade die offensichtliche Lösung gefunden und sie optimiert, bis sie funktioniert hat. Ich habe es für alle Zahlen zwischen 1 und 1 Milliarde getestet und ... es funktioniert. Der Test dauert auf meinem Rechner nur ca. 100 Sekunden.
Das Schöne daran ist, dass, da b, c und d mit verzögerten Zuweisungen definiert sind
:=
, sie nicht neu definiert werden müssen, wenn a dekrementiert wird. Dies ersparte ein paar zusätzliche Zeilen, die ich zuvor hatte. Ich könnte weiter Golf spielen und die überflüssigen Teile verschachteln, aber hier ist der erste Entwurf.Oh, und du läufst es so
S@123456789
und kannst es mit{S@#, Total[(S@#)^2]} & @ 123456789
oder testen# == Total[(S@#)^2]&[123456789]
. Der ausführliche Test istIch habe vorher eine Print [] -Anweisung verwendet, aber das hat sie sehr verlangsamt, obwohl sie nie aufgerufen wird. Stelle dir das vor.
quelle
n - a^2 - b^2 - c^2
als Variable zu speichern und zu überprüfen, ob sied^2
gleich ist.a * 4^(2^k)
zu seink>=2
, dass sie alle Potenzen von 4 extrahiert haben, sodass diesa
kein Vielfaches von 4 ist (aber gerade sein könnte). Darüber hinaus ist jedea
entweder 3 mod 4 oder doppelt so groß. Der kleinste ist 192.Haskell 123 + 3 = 126
Einfache rohe Gewalt über vorberechneten Quadraten.
Es benötigt die
-O
Kompilierungsoption (dazu habe ich 3 Zeichen hinzugefügt). Im schlimmsten Fall dauert es weniger als 1 Minute 999950883.Nur auf GHC getestet.
quelle
C: 198 ZeichenIch kann es wahrscheinlich auf etwas mehr als 100 Zeichen reduzieren. Was ich an dieser Lösung mag, ist die minimale Menge an Junk, nur eine einfache for-Schleife, die das tut, was eine for-Schleife tun sollte (was verrückt sein soll).
Und stark verschönert:
Bearbeiten: Es ist nicht schnell genug für alle Eingaben, aber ich werde mit einer anderen Lösung zurück sein. Ich lasse dieses ternäre Operations-Chaos ab sofort bestehen.
quelle
Rev. B: C, 179
Vielen Dank an @Dennis für die Verbesserungen. Der Rest der Antwort unten ist nicht von Rev. A aktualisiert.
Rev. A: C, 195
Viel schneller als meine andere Antwort und mit viel weniger Gedächtnis!
Dies verwendet http://en.wikipedia.org/wiki/Legendre%27s_three-square_theorem . Jede Zahl, die nicht die folgende Form hat, kann als Summe von 3 Quadraten ausgedrückt werden (ich nenne dies die verbotene Form):
4^a*(8b+7), or equivalently 4^a*(8b-1)
Beachten Sie, dass alle ungeraden Quadratzahlen von der Form sind
(8b+1)
und alle geraden Quadratzahlen oberflächlich von der Form sind4b
. Dies verbirgt jedoch die Tatsache, dass alle geraden quadratischen Zahlen von der Form sind4^a*(odd square)==4^a*(8b+1)
. Als Ergebnis2^x-(any square number < 2^(x-1))
für ungeradex
wird immer die verbotene Form sein. Daher sind diese Zahlen und ihre Vielfachen schwierige Fälle, weshalb so viele der Programme hier als ersten Schritt Potenzen von 4 aufteilen.Wie in der Antwort von @ xnor angegeben,
N-a*a
kann nicht für 2 aufeinanderfolgende Werte von die verbotene Form seina
. Unten stelle ich eine vereinfachte Form seiner Tabelle vor. Zusätzlich zu der Tatsache, dass nach der Division durch 4N%4
nicht gleich 0 sein kann, ist zu beachten, dass es nur 2 mögliche Werte für gibt(a*a)%4
.Wir wollen also vermeiden,
(N-a*a)
dass Werte , die möglicherweise von der verbotenen Form sind, nämlich solche mit(N-a*a)%4
3 oder 0. Wie zu sehen ist, kann diesN
bei ungeraden und geraden Werten nicht für dasselbe auftreten(a*a)
.Mein Algorithmus funktioniert also so:
Ich mag besonders die Art und Weise, wie ich Schritt 3 mache. Ich addiere 3 zu
N
, so dass die Dekrementierung erforderlich ist, wenn(3+N-a*a)%4 =
3 oder 2. (aber nicht 1 oder 0.) Teilen Sie dies durch 2 und die ganze Arbeit kann durch einen ziemlich einfachen Ausdruck erledigt werden .Ungolfed Code
Beachten Sie die einzelne
for
Schleifeq
und die Verwendung von Division / Modulo die Werte abzuleitenb
undc
daraus. Ich habe versucht,a
anstelle von 256 Bytes einen Divisor zu verwenden, aber manchmal stimmte der Wert vona
nicht, und das Programm hing möglicherweise auf unbestimmte Zeit. 256 war der beste Kompromiss, den ich>>8
anstelle/256
für die Teilung verwenden kann.Ausgabe
Eine interessante Eigenheit ist, dass bei Eingabe einer Quadratzahl
N-(a*a)
= 0. Aber das Programm erkennt, dass0%4
= 0 und dekrementiert zum nächsten Quadrat nach unten. Infolgedessen werden Quadratzahleneingaben immer in eine Gruppe kleinerer Quadrate zerlegt, es sei denn, sie haben die Form4^x
.quelle
m=1
Vorher deklarierenmain
. 2. Führen Siescanf
diefor
Anweisung aus. 3. Verwenden Siefloat
anstelle vondouble
. 4.n%4<1
ist kürzer als!(n%4)
. 5. Die Formatzeichenfolge von printf enthält ein veraltetes Leerzeichen.n-=a*a
funktioniert nicht, daa
es nachträglich geändert werden kann (es gibt einige falsche Antworten und hängt an einer kleinen Anzahl von Fällen, wie 100 + 7 = 107). Den Rest habe ich eingeschlossen. Es wäre nett, etwas zu verkürzen,printf
aber ich denke, die einzige Antwort ist, die Sprache zu ändern. Der Schlüssel zur Geschwindigkeit liegt darin, sicha
schnell auf einen guten Wert zu einigen. Geschrieben in C und mit einem Suchraum von weniger als 256 ^ 2 ist dies wahrscheinlich das schnellste Programm hier.printf
Anweisung scheint schwierig zu sein, ohne ein Makro oder ein Array zu verwenden, wodurch an anderer Stelle mehr Masse hinzugefügt würde. Das Wechseln der Sprache scheint der "einfache" Weg zu sein. Ihr Ansatz würde 82 Bytes in CJam wiegen.JavaScript -
175 191 176173 ZeichenBrachiale Kraft, aber schnell.
Schnelle Bearbeitung, aber nicht genug für schlechte Eingaben. Ich musste einen ersten Schritt der Reduzierung um ein Vielfaches von 4 hinzufügen.
Edit 2 Funktion entfernen, Ausgabe innerhalb der Schleife, dann Exit-Contition erzwingen
Edit 3 0 ist keine gültige Eingabe
Ungolfed:
Beispielausgabe
quelle