Je travaille sur le logiciel d'une machine qui coupe automatiquement les ongles des pieds, afin que les utilisateurs puissent simplement y mettre leurs pieds et l'exécuter au lieu d'avoir à le faire manuellement en les mordant ou en utilisant un coupe-ongles.
Un pourcentage important de notre base d'utilisateurs potentiels sera probablement juif et, de toute évidence, il existe une tradition de ne pas couper les ongles des pieds ( ou des ongles ) dans un ordre séquentiel.
Il semble y avoir une opinion dissidente sur l'application précise de cette tradition, mais nous pensons que les règles suivantes sont suffisantes pour accueillir les personnes dont les pratiques religieuses interdisent de couper les ongles dans l'ordre:
- Pas deux ongles adjacents ne doivent être coupés consécutivement
- La séquence de coupe sur le pied gauche ne doit pas correspondre à la séquence sur le pied droit
- La séquence de coupe sur deux passages consécutifs ne doit pas être la même. Les séquences ne devraient pas être facilement prévisibles, donc coder en dur une séquence alternée ne fonctionne pas.
C'est ainsi que nous avons décidé de numéroter les orteils:
5 4 3 2 1 1 2 3 4 5
Left foot Right foot
J'ai écrit du code pour résoudre le problème, mais l'algorithme utilisé est sous-optimal: en fait, le pire des cas est O (∞) . Son fonctionnement est comparable à celui du bogosort . Voici une simplification du pseudocode du code réel utilisé:
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)
Fondamentalement, il génère des séquences aléatoires et vérifie si elles répondent aux critères. S'il ne répond pas aux critères, il recommence. Cela ne prend pas un temps ridiculement long, mais c'est très imprévisible.
Je me rends compte que la façon dont je le fais actuellement est assez terrible, mais j'ai du mal à trouver une meilleure solution. L'un de vous peut-il suggérer un algorithme plus élégant et plus performant?
la source
Réponses:
Vous pouvez générer toutes les séquences de coupe d'ongle d'orteil possibles sans aucune restriction, puis filtrer toutes les séquences qui violent la règle juive. Heureusement, les humains n'ont que cinq orteils par pied *, donc il n'y en a que 5! = 120 séquences sans restriction.
Exemple Python:
Pour appliquer votre règle "pas de répétition de la même séquence", vous pouvez simplement choisir quatre des séquences ci-dessus et les utiliser en alternance. Le seul hic ici est que si vous comptez les deux gros orteils comme "consécutifs", vous ne pouvez pas choisir deux séquences qui se terminent et commencent par 1, respectivement.
* Vous pouvez créer une variable numberOfToesPerFoot afin de pouvoir la modifier facilement plus tard si l'un de vos clients s'avère avoir moins d'orteils que prévu, ou plus.
la source
Il existe un nombre fini de séquences qui répondent à vos besoins.
EDIT: S'il ne s'agit pas vraiment d'orteils, mais d'un problème aléatoire où l'ensemble peut être beaucoup plus grand que 5, l'espace de séquence devient très grand et la chance de répéter la même séquence sur le deuxième pied devient très petite. Donc, générer des séquences au hasard et les rejeter si elles correspondent est une bonne idée. Générer des séquences aléatoires selon une règle comme "sautez par deux ou trois, puis remplissez les blancs" sera probablement plus rapide que de générer des permutations aléatoires et des tests, et le risque de chevauchement sera encore faible si le nombre de "orteils" est grand .
la source
En fait, j'aime mieux votre algorithme d'origine.
Puisque 14 permutations sur 120 fonctionnent, 106 sur 120 ne le font pas. Ainsi, chaque test a une chance d'échouer de 106/120.
Cela signifie que le nombre d'échecs attendu est:
Pas trop difficile de résumer cette série infinie:
Multipliez par x:
Soustraire:
Multipliez à nouveau par x et soustrayez à nouveau:
Puisque x = 106/120, S = 64,9.
Ainsi, en moyenne, votre boucle n'a besoin que de 65 itérations pour trouver une solution.
Quelle est la probabilité que cela prenne, disons, mille itérations?
Eh bien, la probabilité d'échouer à une seule itération est de 104/120, donc la probabilité d'échouer 1000 itérations est de (104/120) ^ 1000, ce qui équivaut à 10 ^ (- 63). Autrement dit, vous ne verrez jamais cela se produire de votre vie, et probablement pas de la vie de l'univers.
Pas de tableaux précalculés, adaptation facile à différents nombres de doigts / orteils / mains / pieds, adaptation facile à différents ensembles de règles ... Qu'est-ce qu'il ne faut pas aimer? La décomposition exponentielle est une chose merveilleuse.
[mettre à jour]
Oups, je me suis trompé dans la formule originale ... Puisque mes probabilités ne totalisent pas 1 :-)
L'expression correcte du nombre d'échecs attendu est:
(Par exemple, pour obtenir exactement deux échecs, vous avez besoin de deux échecs suivis d'un succès . Deux échecs ont une probabilité (106/120) ^ 2; un succès a une probabilité (14/120); multipliez-les ensemble pour obtenir le poids du Terme "2".)
Donc mon S est décalé d'un facteur (1-x) (c'est-à-dire 14/120). Le nombre réel d'échecs attendu est simplement x / (1-x) = 106/14 = 7,57. Ainsi, en moyenne, il ne faut que 8 à 9 itérations pour trouver une solution (7,5 échecs plus un succès).
Mon calcul pour le cas des «1000 échecs» est toujours correct, je pense.
la source
L'évidence: trouvez une commande qui fonctionne et codez-la en dur. Mais je ne pense pas que vous vouliez faire ça.
Vous pouvez générer des permutations bien mieux que la façon dont vous le faites. Vous n'avez pas besoin de faire un échantillonnage de rejet. Utilisez un mélange Fisher Yates sur une permutation initialement triée (1, 2, .. 5) et vous obtiendrez une permutation aléatoire. http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Mais en général, la méthode de génération et de test me semble tout à fait correcte, tant que la probabilité de générer une entrée réussie est suffisamment élevée. Je suis sûr qu'il existe de nombreuses séquences valides selon vos critères, une fois que vous passez à une permutation aléatoire, je doute que vous deviez faire de nombreuses itérations de rejet.
la source
Rien de vraiment nouveau ici, la même solution @Kevin déjà postée, mais je trouve intéressant de voir comment cela se traduit en langage fonctionnel. Dans ce cas, Mathematica :
Quelques explications:
Le résultat final est:
Éditer
Ou, plus difficile à expliquer, mais plus court:
la source
Il n'y a vraiment aucune raison d'introduire le hasard dans ce problème. Il n'y a que 14 séquences qui répondent à ce problème, et un certain ordre de ces séquences satisferait certainement le mieux le sens esthétique que vous essayez d'accommoder. Ainsi, vous devriez simplement réduire ce problème à un algorithme pour choisir une séquence parmi ces 14, probablement dans un ordre prédéfini.
Implémentation Javascript de l'algorithme pour trouver le 14:
EDIT: La nouvelle exigence selon laquelle les séquences doivent être générées de manière aléatoire ne peut pas être facilement satisfaite. Le mieux que vous puissiez probablement faire est de les générer de manière pseudo-aléatoire, ce qui est tout aussi déterministe que de les coder en dur à l'avance, et ne devrait donc satisfaire les superstitions de personne.
la source