Ich arbeite an der Software für eine Maschine, die automatisch die Zehennägel schneidet, sodass Benutzer einfach ihre Füße hineinlegen und sie ausführen können, anstatt sie manuell durch Beißen oder Verwenden von Nagelknipsern ausführen zu müssen.
Ein beträchtlicher Prozentsatz unserer potenziellen Nutzerbasis wird wahrscheinlich jüdisch sein, und es gibt offensichtlich eine Tradition, Zehennägel ( oder Fingernägel ) nicht in sequentieller Reihenfolge zu schneiden
Es scheint abweichende Meinungen über die genaue Anwendung dieser Tradition zu geben, aber wir glauben, dass die folgenden Regeln ausreichen, um Menschen unterzubringen, deren religiöse Praktiken das Schneiden von Zehennägeln verbieten:
- Es sollten keine zwei benachbarten Zehennägel nacheinander geschnitten werden
- Die Schnittsequenz am linken Fuß sollte nicht mit der Sequenz am rechten Fuß übereinstimmen
- Die Schnittsequenz bei zwei aufeinanderfolgenden Läufen sollte nicht gleich sein. Die Sequenzen sollten nicht leicht vorhersehbar sein, daher funktioniert die Hardcodierung einer alternierenden Sequenz nicht.
So haben wir beschlossen, die Zehen zu nummerieren:
5 4 3 2 1 1 2 3 4 5
Left foot Right foot
Ich habe Code geschrieben, um das Problem zu lösen, aber der verwendete Algorithmus ist nicht optimal: Tatsächlich ist die schlechteste Leistung O (∞) . Die Art und Weise, wie es funktioniert, ist vergleichbar mit Bogosort . Hier ist eine Pseudocode-Vereinfachung des tatsächlich verwendeten Codes:
function GenerateRandomSequence
sequence = Array[5]
foreach (item in sequence)
item = RandomNumberBetween(1,5)
return sequence
function GetToenailCuttingOrder
while (true)
sequence = GenerateRandomSequence()
if (!AllItemsAreUnique(sequence))
continue
if (NoTwoAdjacentItemsHaveConsecutiveNumbers(sequence))
return sequence
do
leftFootSequence = GetToenailCuttingOrder()
rightFootSequence = GetToenailCuttingOrder()
until (leftFootSequence != rightFootSequence &&
leftFootSequence != leftFootSequenceFromLastRun &&
rightFootSequence != rightFootSequenceFromLastRun)
Grundsätzlich werden zufällige Sequenzen generiert und geprüft, ob diese die Kriterien erfüllen. Wenn es die Kriterien nicht erfüllt, beginnt es von vorne. Es dauert nicht lächerlich lange, ist aber sehr unvorhersehbar.
Mir ist klar, dass die Art und Weise, wie ich es derzeit mache, ziemlich schrecklich ist, aber ich habe Probleme, einen besseren Weg zu finden. Kann jemand von euch einen eleganteren und performanteren Algorithmus vorschlagen?
quelle
Antworten:
Sie können alle möglichen Zehennagel-Schnittsequenzen ohne Einschränkungen generieren und dann alle Sequenzen herausfiltern, die gegen die jüdische Regel verstoßen. Glücklicherweise haben Menschen nur fünf Zehen pro Fuß *, also gibt es nur fünf! = 120 uneingeschränkte Sequenzen.
Python-Beispiel:
Um Ihre Regel "Keine Wiederholungen derselben Sequenz" durchzusetzen, können Sie einfach vier der oben genannten Sequenzen auswählen und abwechselnd verwenden. Der einzige Haken dabei ist, dass Sie, wenn Sie die beiden großen Zehen als "aufeinanderfolgend" zählen, nicht zwei Sequenzen auswählen können, die mit 1 enden bzw. beginnen.
* Möglicherweise möchten Sie eine Variable numberOfToesPerFoot erstellen, damit Sie sie später problemlos ändern können, wenn sich herausstellt, dass einer Ihrer Kunden weniger oder mehr Zehen als erwartet hat.
quelle
Es gibt eine endliche Anzahl von Sequenzen, die Ihren Anforderungen entsprechen.
BEARBEITEN: Wenn es nicht wirklich um Zehen geht, sondern um ein zufälliges Problem, bei dem der Satz viel größer als 5 sein kann, wird der Sequenzraum sehr groß und die Chance, dieselbe Sequenz auf dem zweiten Fuß zu wiederholen, wird sehr gering. Es ist daher eine gute Idee, Sequenzen zufällig zu generieren und sie abzulehnen, wenn sie übereinstimmen. Das Erzeugen von Zufallssequenzen nach einer Regel wie "zu zweit oder zu dritt hüpfen, dann die Lücken ausfüllen" ist wahrscheinlich schneller als das Erzeugen von Zufallspermutationen und Tests, und die Wahrscheinlichkeit einer Überlappung ist immer noch gering, wenn die Anzahl der "Zehen" groß ist .
quelle
Eigentlich gefällt mir Ihr ursprünglicher Algorithmus am besten.
Da 14 von 120 Permutationen funktionieren, funktionieren 106 von 120 nicht. Jeder Scheck hat also eine Wahrscheinlichkeit von 106/120, dass er fehlschlägt.
Das heißt, die erwartete Anzahl von Fehlern ist:
Nicht zu schwer, diese unendliche Reihe zusammenzufassen:
Mit x multiplizieren:
Subtrahieren:
Mit x erneut multiplizieren und erneut subtrahieren:
Da x = 106/120 ist, ist S = 64,9.
Im Durchschnitt benötigt Ihre Schleife also nur 65 Iterationen , um eine Lösung zu finden.
Wie hoch ist die Wahrscheinlichkeit, dass beispielsweise tausend Iterationen erforderlich sind?
Nun, die Wahrscheinlichkeit, dass eine einzelne Iteration fehlschlägt, beträgt 104/120, also beträgt die Wahrscheinlichkeit, dass 1000 Iterationen fehlschlagen, (104/120) ^ 1000, was ungefähr 10 ^ (- 63) entspricht. Das heißt, Sie werden es niemals in Ihrem Leben sehen und wahrscheinlich nicht im Leben des Universums.
Keine vorberechneten Tabellen, einfache Anpassung an unterschiedliche Anzahlen von Fingern / Zehen / Händen / Füßen, einfache Anpassung an unterschiedliche Regelsätze ... Was mag man nicht? Exponentieller Zerfall ist eine wunderbare Sache.
[aktualisieren]
Hoppla, ich habe die ursprüngliche Formel falsch verstanden ... Da sich meine Wahrscheinlichkeiten nicht zu 1 summieren :-)
Der richtige Ausdruck für die erwartete Anzahl von Fehlern lautet:
(Um genau zwei Fehler zu erhalten, benötigen Sie beispielsweise zwei Fehler, gefolgt von einem Erfolg . Zwei Fehler haben eine Wahrscheinlichkeit (106/120) ^ 2; ein Erfolg hat eine Wahrscheinlichkeit (14/120); multiplizieren Sie diese, um das Gewicht für die zu erhalten "2" Begriff.)
Mein S ist also um einen Faktor von (1-x) (dh 14/120) versetzt. Die tatsächlich erwartete Anzahl von Fehlern beträgt nur x / (1-x) = 106/14 = 7,57. Im Durchschnitt sind nur 8-9 Iterationen erforderlich , um eine Lösung zu finden (7,5 Fehler plus ein Erfolg).
Meine Mathematik für den Fall "1000 Fehler" ist immer noch korrekt, denke ich.
quelle
Das Offensichtliche: Finden Sie eine Bestellung, die funktioniert, und codieren Sie sie fest. Aber ich glaube nicht, dass du das machen willst.
Sie können Permutationen viel besser erzeugen als Sie es tun. Sie müssen keine Ablehnungsabtastung durchführen. Verwenden Sie ein Fisher Yates-Shuffle für eine anfänglich sortierte Permutation (1, 2, .. 5), und Sie erhalten eine zufällige Permutation. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Aber im Allgemeinen scheint mir die Generierungs- und Testmethode völlig in Ordnung zu sein, solange die Wahrscheinlichkeit, einen erfolgreichen Eintrag zu generieren, hoch genug ist. Ich bin mir sicher, dass es nach Ihren Kriterien viele gültige Sequenzen gibt. Wenn Sie zu einer zufälligen Permutation wechseln, bezweifle ich, dass Sie viele Ablehnungsiterationen durchführen müssen.
quelle
Hier gibt es nichts wirklich Neues, die gleiche Lösung, die @Kevin bereits veröffentlicht hat, aber ich finde es interessant zu sehen, wie sie in eine funktionale Sprache übersetzt wird. In diesem Fall ist Mathematica :
Einige Erklärungen:
Das Endergebnis ist:
Bearbeiten
Oder schwieriger zu erklären, aber kürzer:
quelle
Es gibt wirklich keinen Grund, Zufälligkeit in dieses Problem einzuführen. Es gibt nur 14 Sequenzen, die dieses Problem erfüllen, und sicherlich würde eine gewisse Reihenfolge dieser Sequenzen den ästhetischen Sinn, den Sie berücksichtigen möchten, am besten erfüllen. Daher sollten Sie dieses Problem nur auf einen Algorithmus zum Auswählen einer Sequenz aus diesen 14 reduzieren, wahrscheinlich in einer voreingestellten Reihenfolge.
Javascript-Implementierung eines Algorithmus zum Finden der 14:
EDIT: Die neue Anforderung, dass die Sequenzen zufällig generiert werden müssen, kann nicht einfach erfüllt werden. Das Beste, was Sie wahrscheinlich tun können, ist, sie pseudozufällig zu generieren, was genauso deterministisch ist, wie sie im Voraus hart zu codieren, und daher den Aberglauben von niemandem befriedigen sollte.
quelle