Geschichte
Mein Unternehmen verschickt wöchentlich einen Newsletter an alle Mitarbeiter des Unternehmens. In diesen Newslettern ist ein Rätsel enthalten, zusammen mit einem Gruß an denjenigen im Unternehmen, der als erster eine E-Mail verschickt / eine Lösung für das Rätsel der letzten Woche bereitgestellt hat. Die meisten dieser Rätsel sind ziemlich trivial und ehrlich gesagt ziemlich langweilig für ein Technologieunternehmen, aber vor einigen Monaten gab es eines, das meine Aufmerksamkeit auf sich zog.
Das ursprüngliche Rätsel:
Angesichts der folgenden Form:
Sie haben die natürlichen Zahlen von 1 bis 16. Passen Sie sie alle in diese Form ein, sodass alle zusammenhängenden Zeilen und zusammenhängenden Spalten zusammen 29 ergeben.
Eine solche Lösung für dieses Rätsel (die "kanonische" Lösung, die ich dem Newsletter vorgelegt habe) war beispielsweise die folgende:
Im Verlauf der Lösung habe ich jedoch einige interessante Informationen gefunden:
- Es gibt deutlich mehr Lösungen als nur diese. Tatsächlich gibt es 9.368 Lösungen.
- Wenn Sie den Regelsatz so erweitern, dass nur Zeilen und Spalten gleich sind und nicht unbedingt 29, erhalten Sie 33.608 Lösungen:
- 4.440 Lösungen für eine Summe von 27.
- 7.400 Lösungen für eine Summe von 28.
- 9.368 Lösungen für eine Summe von 29.
- 6.096 Lösungen für eine Summe von 30.
- 5.104 Lösungen für eine Summe von 31.
- 1.200 Lösungen für eine Summe von 32.
Also machten ich und meine Mitarbeiter (obwohl größtenteils nur mein Manager, da er der einzige andere als ich mit Programmierkenntnissen für allgemeine Zwecke war) uns auf eine Herausforderung, die den größten Teil des Monats dauerte - wir hatten andere, tatsächliche Jobs. damit verbundene Verpflichtungen mussten wir erfüllen - um zu versuchen, ein Programm zu schreiben, das jede einzelne Lösung auf dem schnellstmöglichen Weg findet.
Ursprüngliche Statistik
Das allererste Programm, das ich geschrieben habe, um das Problem zu lösen, überprüfte einfach immer wieder zufällige Lösungen und hörte auf, als es eine Lösung fand. Wenn Sie eine mathematische Analyse zu diesem Problem durchgeführt haben, wissen Sie wahrscheinlich bereits, dass dies nicht hätte funktionieren sollen. Aber irgendwie hatte ich Glück und das Programm brauchte nur eine Minute, um eine einzige Lösung zu finden (die, die ich oben gepostet hatte). Wiederholte Durchläufe des Programms dauerten oft 10 oder 20 Minuten, so dass dies offensichtlich keine rigorose Lösung für das Problem war.
Ich wechselte zu einer rekursiven Lösung, die jede mögliche Veränderung des Puzzles durchlief, und verwarf viele Lösungen auf einmal, indem ich Summen eliminierte, die sich nicht summierten. Wenn die erste Zeile / Spalte, die ich gerade verglich, nicht gleich war, konnte ich sofort aufhören, diesen Zweig zu überprüfen, da ich wusste, dass nichts anderes, das in das Puzzle permutierte, dies ändern würde.
Mit diesem Algorithmus hatte ich den ersten "richtigen" Erfolg: Das Programm konnte in etwa 5 Minuten alle 33.608 Lösungen generieren und ausspucken.
Mein Manager verfolgte einen anderen Ansatz: Als er wusste, dass die einzig möglichen Lösungen Summen von 27, 28, 29, 30, 31 oder 32 enthielten, schrieb er eine Multithread-Lösung, die mögliche Summen nur für diese spezifischen Werte überprüfte. Er schaffte es, sein Programm in nur 2 Minuten zum Laufen zu bringen. Also iterierte ich noch einmal; Ich habe alle möglichen 3/4-stelligen Summen (zu Beginn des Programms; sie werden in der Gesamtlaufzeit gezählt) gehasht und die "Teilsumme" einer Zeile verwendet, um den verbleibenden Wert basierend auf einer zuvor abgeschlossenen Zeile nachzuschlagen, anstatt Testen aller verbleibenden Werte und Verkürzen der Zeit auf 72 Sekunden. Mit etwas Multithreading-Logik habe ich es dann auf 40 Sekunden gebracht. Mein Manager hat das Programm mit nach Hause genommen, einige Optimierungen an der Programmausführung vorgenommen und seine Geschwindigkeit auf 12 Sekunden gesenkt. Ich habe die Auswertung der Zeilen und Spalten neu angeordnet,
Das schnellste Programm, das einer von uns nach einem Monat erhielt, war für meinen Manager 0,15 Sekunden und für mich 0,33 Sekunden. Ich landete behauptet , dass mein Programm war schneller obwohl, da mein Manager Programm, während es tat finden alle Lösungen, druckte sie nicht in eine Textdatei aus. Wenn er diese Logik zu seinem Code hinzufügte, dauerte es oft mehr als 0,4 bis 0,5 Sekunden.
Wir haben unsere intrapersonale Herausforderung seitdem bestehen lassen, aber natürlich bleibt die Frage: Kann dieses Programm schneller gemacht werden?
Das ist die Herausforderung, die ich euch stellen werde.
Deine Herausforderung
Die Parameter, unter denen wir gearbeitet haben, haben die "Summe von 29" -Regel gelockert und sind stattdessen "alle Zeilen / Spalten gleich", und ich werde diese Regel auch für euch festlegen. Die Herausforderung lautet daher: Schreiben Sie ein Programm, das in kürzester Zeit alle Lösungen für dieses Rätsel findet (und druckt!). Ich werde eine Obergrenze für eingereichte Lösungen festlegen: Wenn das Programm auf einem relativ anständigen Computer (<8 Jahre alt) länger als 10 Sekunden dauert, ist es wahrscheinlich zu langsam, um gezählt zu werden.
Außerdem habe ich ein paar Boni für das Rätsel:
- Können Sie die Lösung so verallgemeinern, dass sie für jeden Satz von 16 Zahlen funktioniert, nicht nur für
int[1,16]
? Die Timing-Bewertung würde auf der Grundlage der ursprünglich festgelegten Eingabeaufforderungsnummer ausgewertet, jedoch über diesen Codepfad weitergeleitet. (-10%) - Können Sie den Code so schreiben, dass er mit doppelten Zahlen ordnungsgemäß umgeht und diese auflöst? Dies ist nicht so einfach, wie es scheint! Lösungen, die "visuell identisch" sind, sollten in der Ergebnismenge eindeutig sein. (-5%)
- Kannst du mit negativen Zahlen umgehen? (-5%)
Sie können auch versuchen , eine Lösung zu generieren, die mit Gleitkommazahlen umgehen kann. Lassen Sie sich jedoch nicht schockieren, wenn dies völlig fehlschlägt. Wenn Sie jedoch eine robuste Lösung finden, ist dies möglicherweise einen großen Bonus wert!
In jeder Hinsicht gelten "Rotationen" als einzigartige Lösungen. Eine Lösung, bei der es sich nur um eine Rotation einer anderen Lösung handelt, gilt als ihre eigene Lösung.
Die IDEs, mit denen ich auf meinem Computer arbeite, sind Java und C ++. Ich kann Antworten aus anderen Sprachen akzeptieren, aber Sie müssen möglicherweise auch einen Link angeben, über den ich eine einfach einzurichtende Laufzeitumgebung für Ihren Code erhalten kann.
quelle
Antworten:
C - in der Nähe von 0,5 s
Dieses sehr naive Programm gibt alle Lösungen in einer halben Sekunde auf meinem 4 Jahre alten Laptop. Kein Multithread, kein Hashing.
Windows 10, Visual Studio 2010, CPU-Kern I7 64 Bit
Versuchen Sie es online auf ideone
quelle
int inuse[16];
mit nur ersetzenint inuse;
und dann bitweise Operatoren verwenden, um es zu manipulieren. Es scheint nicht die Geschwindigkeit zu erhöhen , dass viel, aber es hilft ein wenig.dumbbench --precision=.01 -vvv --initial=500 ./solve
C ++ - 300 Millisekunden
Auf Wunsch habe ich meinen eigenen Code hinzugefügt, um dieses Rätsel zu lösen. Auf meinem Computer dauert die Taktung durchschnittlich 0,310 Sekunden (310 Millisekunden), kann jedoch je nach Abweichung bis zu 287 Millisekunden dauern. Ich sehe es sehr selten über 350 Millisekunden ansteigen, normalerweise nur, wenn mein System mit einer anderen Aufgabe überfordert ist.
Diese Zeiten basieren auf der im Programm verwendeten Selbstmeldung, ich habe sie jedoch auch mit einem externen Timer getestet und erhalte ähnliche Ergebnisse. Der Overhead im Programm scheint etwa 10 Millisekunden hinzuzufügen.
Auch dann , wenn mein Code nicht ganz Duplikate korrekt verarbeiten. Es kann mit ihnen gelöst werden, aber es werden keine "visuell identischen" Lösungen aus dem Lösungssatz entfernt.
quelle
0.1038s +/- 0.0002
Und hier ist die Zeit für Ihren Code mit vereinfachter Ausgabe:0.0850s +/- 0.0001
Sie können also ~ 18 ms sparen, zumindest auf meinem Computer. Ich habe beide Versionen mehr als 500 Mal mit rausgeworfenen Ausreißern mit Dumbbench ausgeführtProlog - 3 Minuten
Diese Art von Puzzle scheint ein perfekter Anwendungsfall für Prolog zu sein. Also habe ich in Prolog eine Lösung programmiert! Hier ist es:
Leider ist es nicht so schnell wie ich erwartet hatte. Vielleicht kann jemand, der sich mit deklarativer Programmierung (oder speziell mit Prolog) auskennt, einige Optimierungstipps anbieten. Sie können die Regel
puzzle
mit dem folgenden Befehl aufrufen :Probieren Sie es hier online aus . Sie können eine beliebige Zahl anstelle des
29
s im Code einsetzen, um alle Lösungen zu generieren. So wie es aussieht, werden alle 29-Lösungen in ungefähr 30 Sekunden gefunden. Um alle möglichen Lösungen zu finden, sollten Sie ungefähr 3 Minuten brauchen.quelle