J'ai n éléments. À titre d'exemple, disons, 7 éléments, 1234567. Je sais qu'il y en a 7! = 5040 permutations possibles de ces 7 éléments.
Je veux un algorithme rapide comprenant deux fonctions:
f (nombre) associe un nombre entre 0 et 5039 à une permutation unique, et
f '(permutation) associe la permutation au nombre à partir duquel elle a été générée.
Je me fiche de la correspondance entre le nombre et la permutation, à condition que chaque permutation ait son propre numéro unique.
Ainsi, par exemple, je pourrais avoir des fonctions où
f(0) = '1234567'
f'('1234567') = 0
L'algorithme le plus rapide qui me vient à l'esprit est d'énumérer toutes les permutations et de créer une table de recherche dans les deux sens, de sorte qu'une fois les tables créées, f (0) serait O (1) et f ('1234567') serait un recherche sur une chaîne. Cependant, cela demande de la mémoire, en particulier lorsque n devient grand.
Quelqu'un peut-il proposer un autre algorithme qui fonctionnerait rapidement et sans inconvénient de mémoire?
Réponses:
Pour décrire une permutation de n éléments, vous voyez que pour la position à laquelle le premier élément se termine, vous avez n possibilités, vous pouvez donc le décrire avec un nombre compris entre 0 et n-1. Pour la position à laquelle se termine l'élément suivant, vous avez n-1 possibilités restantes, vous pouvez donc la décrire avec un nombre compris entre 0 et n-2.
Et cetera jusqu'à ce que vous ayez n nombres.
À titre d'exemple pour n = 5, considérons la permutation qui conduit
abcde
àcaebd
.a
, le premier élément, se termine à la deuxième position, nous lui attribuons donc l'indice 1 .b
finit à la quatrième position, qui serait l'indice 3, mais c'est la troisième qui reste, donc nous lui attribuons 2 .c
se termine à la première position restante, qui est toujours 0 .d
se termine à la dernière position restante, qui (sur seulement deux positions restantes) est 1 .e
se retrouve à la seule position restante, indexée à 0 .Nous avons donc la séquence d'index {1, 2, 0, 1, 0} .
Vous savez maintenant que par exemple dans un nombre binaire, «xyz» signifie z + 2y + 4x. Pour un nombre décimal,
c'est z + 10y + 100x. Chaque chiffre est multiplié par un poids et les résultats sont additionnés. Le modèle évident dans le poids est bien sûr que le poids est w = b ^ k, avec b la base du nombre et k l'indice du chiffre. (Je compterai toujours les chiffres à partir de la droite et en commençant à l'index 0 pour le chiffre le plus à droite. De même, quand je parle du «premier» chiffre, je veux dire le plus à droite.)
La raison pour laquelle les pondérations des chiffres suivent ce modèle est que le nombre le plus élevé qui peut être représenté par les chiffres de 0 à k doit être exactement 1 inférieur au nombre le plus bas qui peut être représenté en utilisant uniquement le chiffre k + 1. En binaire, 0111 doit être un inférieur à 1000. En décimal, 099999 doit être un inférieur à 100000.
Encodage en base variable
L'espacement entre les nombres suivants étant exactement de 1 est la règle importante. En réalisant cela, nous pouvons représenter notre séquence d'index par un nombre à base variable . La base de chaque chiffre correspond au nombre de possibilités différentes pour ce chiffre. Pour la décimale, chaque chiffre a 10 possibilités, pour notre système le chiffre le plus à droite aurait 1 possibilité et le plus à gauche aurait n possibilités. Mais comme le chiffre le plus à droite (le dernier nombre de notre séquence) est toujours 0, nous l'oublions. Cela signifie qu'il nous reste les bases 2 à n. En général, le kème chiffre aura la base b [k] = k + 2. La valeur la plus élevée autorisée pour le chiffre k est h [k] = b [k] - 1 = k + 1.
Notre règle sur les poids w [k] des chiffres exige que la somme de h [i] * w [i], où i va de i = 0 à i = k, soit égale à 1 * w [k + 1]. En termes récurrents, w [k + 1] = w [k] + h [k] * w [k] = w [k] * (h [k] + 1). Le premier poids w [0] doit toujours être 1. À partir de là, nous avons les valeurs suivantes:
(La relation générale w [k-1] = k! Est facilement prouvée par récurrence.)
Le nombre que nous obtenons en convertissant notre séquence sera alors la somme de s [k] * w [k], avec k allant de 0 à n-1. Ici, s [k] est le k'e élément (le plus à droite, commençant à 0) de la séquence. À titre d'exemple, prenons notre {1, 2, 0, 1, 0}, avec l'élément le plus à droite retiré comme mentionné précédemment: {1, 2, 0, 1} . Notre somme est 1 * 1 + 0 * 2 + 2 * 6 + 1 * 24 = 37 .
Notez que si nous prenons la position maximale pour chaque index, nous aurions {4, 3, 2, 1, 0}, et cela se convertit en 119. Puisque les poids de notre encodage numérique ont été choisis de manière à ne pas sauter tous les nombres, tous les nombres de 0 à 119 sont valides. Il y en a précisément 120, soit n! pour n = 5 dans notre exemple, précisément le nombre de permutations différentes. Ainsi, vous pouvez voir nos nombres encodés spécifier complètement toutes les permutations possibles.
Décodage à partir de la base de variables Le
décodage est similaire à la conversion en binaire ou décimal. L'algorithme commun est le suivant:
Pour notre numéro à base variable:
Cela décode correctement nos 37 en {1, 2, 0, 1} (ce
sequence
serait{1, 0, 2, 1}
dans cet exemple de code, mais peu importe ... tant que vous indexez correctement). Il suffit d'ajouter 0 à l'extrémité droite (rappelez-vous que le dernier élément n'a toujours qu'une seule possibilité pour sa nouvelle position) pour récupérer notre séquence d'origine {1, 2, 0, 1, 0}.Permutation d'une liste à l'aide d'une séquence d'index
Vous pouvez utiliser l'algorithme ci-dessous pour permuter une liste en fonction d'une séquence d'index spécifique. C'est un algorithme O (n²), malheureusement.
Représentation commune des permutations
Normalement, vous ne représenteriez pas une permutation de manière aussi imprudente que nous l'avons fait, mais simplement par la position absolue de chaque élément après l'application de la permutation. Notre exemple {1, 2, 0, 1, 0} pour
abcde
tocaebd
est normalement représenté par {1, 3, 0, 4, 2}. Chaque indice de 0 à 4 (ou en général de 0 à n-1) apparaît exactement une fois dans cette représentation.L'application d'une permutation sous cette forme est facile:
L'inverser est très similaire:
Conversion de notre représentation à la représentation commune
Notez que si nous prenons notre algorithme pour permuter une liste en utilisant notre séquence d'index, et l'appliquons à la permutation d'identité {0, 1, 2, ..., n-1}, nous obtenons le permutation inverse , représentée sous la forme courante. ( {2, 0, 4, 1, 3} dans notre exemple).
Pour obtenir la prémutation non inversée, nous appliquons l'algorithme de permutation que je viens de montrer:
Ou vous pouvez simplement appliquer la permutation directement, en utilisant l'algorithme de permutation inverse:
Notez que tous les algorithmes pour traiter les permutations sous la forme courante sont O (n), tandis que l'application d'une permutation sous notre forme est O (n²). Si vous devez appliquer une permutation plusieurs fois, convertissez-la d'abord en représentation commune.
la source
1234
, f (4) = {0, 2, 0, 0} = 1342. Et f '(1423) = {0, 1 1, 0} = 3. Cet algorithme est vraiment inspirant. Je me demande que ce soit l'œuvre originale de l'OP. je l'ai étudié et analysé pendant un certain temps. Et je crois que c'est correct :){1, 2, 0, 1, 0}
->{1, 3, 0, 4, 2}
? Et vice versa? C'est possible? (en ne convertissant pas entre{1, 2, 0, 1, 0}
<-->{C, A, E, B, D}
, ce qui nécessite O (n ^ 2).) Si "notre style" et "notre style commun" ne sont pas convertibles, ce sont en fait deux choses distinctes, n'est-ce pas? Merci xJ'ai trouvé un algorithme O (n), voici une brève explication http://antoinecomeau.blogspot.ca/2014/07/mapping-between-permutations-and.html
la source
La complexité peut être ramenée à n * log (n), voir section 10.1.1 ("Le code Lehmer (table d'inversion)", p.232ff) du fxtbook: http://www.jjj.de/fxt/ #fxtbook passez à la section 10.1.1.1 ("Calcul avec de grands tableaux" p.235) pour la méthode rapide. Le code (GPL, C ++) se trouve sur la même page Web.
la source
Problème résolu. Cependant, je ne suis pas sûr que vous ayez encore besoin de la solution après ces années. LOL, je viens de rejoindre ce site, alors ... Vérifiez ma classe de permutation Java. Vous pouvez vous baser sur un index pour obtenir une permutation de symbole, ou donner une permutation de symbole puis obtenir l'index.
Voici ma classe de prémutation
et voici ma classe principale pour montrer comment utiliser la classe.
S'amuser. :)
la source
Chaque élément peut être dans l'une des sept positions. Pour décrire la position d'un élément, vous auriez besoin de trois bits. Cela signifie que vous pouvez stocker la position de tous les éléments dans une valeur 32 bits. C'est loin d'être efficace, puisque cette représentation permettrait même à tous les éléments d'être dans la même position, mais je pense que le masquage de bits devrait être raisonnablement rapide.
Cependant, avec plus de 8 postes, vous aurez besoin de quelque chose de plus astucieux.
la source
Cela se trouve être une fonction intégrée dans J :
la source
Vous pouvez encoder des permutations à l'aide d'un algorithme récursif. Si une N-permutation (un certain ordre des nombres {0, .., N-1}) est de la forme {x, ...} alors codez-la comme x + N * le codage du (N-1) -permutation représentée par "..." sur les nombres {0, N-1} - {x}. Cela ressemble à une bouchée, voici un code:
Cet algorithme est O (n ^ 2). Points bonus si quelqu'un a un algorithme O (n).
la source
Quelle question intéressante!
Si tous vos éléments sont des nombres, vous pouvez envisager de les convertir de chaînes en nombres réels. Ensuite, vous pourrez trier toutes les permutations en les mettant dans l'ordre et les placer dans un tableau. Après cela, vous seriez ouvert à l'un des différents algorithmes de recherche disponibles.
la source
J'étais précipité dans ma réponse précédente (supprimée), j'ai la réponse réelle cependant. Il est fourni par un concept similaire, la factoradique , et est lié aux permutations (ma réponse relative aux combinaisons, je m'excuse pour cette confusion). Je déteste simplement publier des liens wikipedia, mais j'ai écrit que je l'ai fait il y a quelque temps est inintelligible pour une raison quelconque. Donc, je peux développer cela plus tard si demandé.
la source
Il y a un livre écrit à ce sujet. Désolé, mais je ne me souviens plus du nom de celui-ci (vous le trouverez très probablement sur wikipedia). mais de toute façon j'ai écrit une implémentation python de ce système d'énumération: http://kks.cabal.fi/Kombinaattori Une partie est en finnois, mais copiez simplement le code et les variables de nom ...
la source
J'avais cette question exacte et j'ai pensé que je fournirais ma solution Python. C'est O (n ^ 2).
C'est assez simple; après avoir généré la représentation factorielle du nombre, je viens de choisir et de supprimer les caractères de la chaîne. La suppression de la chaîne est la raison pour laquelle il s'agit d'une solution O (n ^ 2).
La solution d'Antoine est meilleure pour la performance.
la source
Une question connexe est le calcul de la permutation inverse, une permutation qui restaurera les vecteurs permutés dans l'ordre d'origine lorsque seul le tableau de permutation est connu. Voici le code O (n) (en PHP):
Logiciel de printemps de David Spector
la source