Nombre de n-élément de permutations avec exactement k inversions
Je suis en train d'essayer de résoudre efficacement les SPOJ Problème 64: Permutations.
Let A = [a1,a2,...,an] une permutation des entiers 1,2,...,n. Une paire
d'indices (i,j), 1<=i<=j<=n, est une inversion de la permutation d'Un si
ia>aj. Nous sont donnés des entiers n>0 et k>=0. Quel est le nombre de
n-élément permutations contenant exactement k inversions?Par exemple, le nombre de 4 éléments de permutations avec exactement 1
l'inversion est égal à 3.
Pour prendre l'exemple donné plus facile à voir, voici les trois 4-élément de permutations avec exactement 1 inversion:
(1, 2, 4, 3)
(1, 3, 2, 4)
(2, 1, 3, 4)
Dans la première permutation, 4 > 3 et l'indice de 4 est inférieur à l'indice de 3. C'est une simple inversion. Depuis la permutation a exactement une inversion, il est l'un des permutations que nous essayons de compter.
Pour chaque séquence de n éléments, le nombre de permutations est factoriel(n). Donc si j'utilise la force brute, n2 la façon de compter le nombre d'inversions pour chaque permutation et puis de vérifier pour voir si ils sont égaux à k, la solution à ce problème aurait le temps de complexité O(n! * n2).
La Recherche Précédente
Un subproblem de ce problème a été déjà demandé ici sur StackOverflow. Un O(n log n) solution à l'aide de fusion de tri a été faite, qui compte le nombre d'inversions dans un unique permutation. Cependant, si j'utilise cette solution pour compter le nombre d'inversions pour chaque permutation, je voudrais encore une fois la complexité de O(n! * n log n), qui est encore très élevé à mon avis.
Mon objectif est d'éviter la complexité factorielle qui vient d'itérer sur toutes les permutations. Idéalement, je voudrais une formule mathématique qui donne la réponse à cette pour tout n et k, mais je ne suis pas sûr si l'un existe même.
Si il n'y a pas de mathématiques formule pour résoudre ce (que je doute) puis j'ai aussi vu des gens donner des conseils que l'efficacité de la programmation dynamique solution est possible. À l'aide de la photographie ou d'une autre approche, j'aimerais vraiment trouver une solution qui est plus efficace que O(n! * n log n), mais je ne suis pas sûr où commencer.
Tous les conseils, commentaires, ou suggestions sont les bienvenus.
EDIT: j'ai répondu au problème ci-dessous avec une approche DP à l'informatique Mahonian numéros.
- Le fait que cela a plus de 1.4 k vues et seulement dix upvotes est stupéfiant, l'excellence de la recherche sur tous les comptes, +1
Vous devez vous connecter pour publier un commentaire.
La solution a besoin de quelques explications.
Nous allons désigner le nombre de permutations de n éléments ayant exactement k inversions
par I(n, k)
Maintenant, je(n, 0) est toujours de 1. Pour tout n, il existe une et une seule permutation qui a 0
les inversions c'est à dire, lorsque la séquence est de plus en plus triés
Maintenant j'(0, k) est toujours à 0 puisque nous n'avons pas la séquence elle-même
Maintenant, pour trouver les I(n, k) nous allons prendre un exemple de séquence contenant 4 éléments
{1,2,3,4}
pour n = 4 ci-dessous sont les permutations énumérés et regroupés par nombre d'inversions
Maintenant, pour trouver le nombre de permutation avec n = 5 et pour tout k
on peut en déduire la récurrence I(5, k) à partir de I(4, k) par l'insertion, le n-ième (la plus grande)
élément(5) quelque part dans chaque permutation dans le précédent permutations,
de sorte que le nombre d'inversions est k
par exemple, j'ai(5,4) n'est rien, mais le nombre de permutations de la séquence {1,2,3,4,5}
qui a exactement 4 inversions de chacun.
Observons I(4, k) au-dessus jusqu'à ce que la colonne k = 4 le nombre d'inversions est <= 4
Maintenant permet de placer l'élément 5, comme illustré ci-dessous
Chaque permutation qui contient 5 a exactement 4 inversions.
De sorte que le total de permutation avec 4 inversions I(5,4) = I(4,4) + I(4,3) + I(4,2) + I(4,1) + I(4,0)
= 1 + 3 + 5 + 6 + 5 = 20
De même pour I(5,5) à partir de I(4,k)
De sorte que le total de permutation avec 5 inversions I(5,5) = I(4,5) + I(4,4) + I(4,3) + I(4,2) + I(4,1)
= 3 + 5 + 6 + 5 + 3 = 22
Donc
I(n, k) = sum of I(n-1, k-i) such that i < n && k-i >= 0
Également, k peut aller jusqu'à n*(n-1)/2, cela se produit lorsque la séquence est triée dans l'ordre décroissant
https://secweb.cs.odu.edu/~zeil/cs361/web/website/Lectures/insertion/pages/ar01s04s01.html
http://www.algorithmist.com/index.php/SPOJ_PERMUT1
C'est un jour plus tard, et j'ai réussi à résoudre le problème en utilisant la programmation dynamique. Je l'ai soumis et mon code a été accepté par SPOJ donc je me dis que je vais partager mes connaissances ici pour quiconque est intéressé à l'avenir.
Après avoir cherché dans l' Page Wikipedia qui traite de l'inversion en mathématiques discrètes, j'ai trouvé une intéressante recommandation au bas de la page.
J'ai cliqué sur le lien pour OEIS et il m'a montré une suite infinie d'entiers appelé le Triangle de Mahonian numéros.
J'étais curieux de savoir ce que ces chiffres, car ils semblaient familier pour moi. Puis j'ai réalisé que j'avais vu que la sous-suite 1, 3, 5, 6, 5, 3, 1 avant de. En fait, c'était la solution du problème pour plusieurs couples (n, k), à savoir (4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6). J'ai regardé ce qui était sur les deux côtés de cette sous-suite et a été étonné de voir qu'il était valide (c'est à dire supérieure à 0 permutations) réponses pour le n < 4 et n > 4.
La formule pour la séquence a été donnée sous la forme:
C'était assez facile pour moi de comprendre et de vérifier. Je pourrais en gros, prendre tout n et de le brancher dans la formule. Le coefficient pour la xk terme serait la solution pour (n, k).
Je vais vous montrer un exemple pour n = 3.
(x0)(x0 + 1)(x0 + x1 + x2)
= (1)(1 + x)(1 + x + x2)
= (1 + x)(1 + x + x2)
= 1 + x + x + x2 + x2 + x3
= 1 + 2x + 2x2 + x3
La dernière extension a été
1 + 2x + 2x2 + x3
et les coefficients de xk conditions étaient de 1, 2, 2, et 1 pour k = 0, 1, 2, 3 respectivement. Cela se produit juste pour être valide tous les numéros de inversions pour les 3-élément de permutations.1, 2, 2, 1 est la 3ème ligne de la Mahonian numéros lorsqu'ils sont disposés dans un tableau comme suit:
Donc, fondamentalement, le calcul de ma réponse se résume simplement à calculer le n-ième Mahonian de ligne et prendre la kième élément k de départ à 0 et l'impression de 0 si l'index est hors de la plage. C'était une simple affaire de bottom-up de la programmation dynamique, puisque chaque i-ème ligne pourrait être utilisé pour calculer facilement le i+1ère ligne.
Donné ci-dessous est le Python solution que j'ai utilisé qui a couru seulement 0,02 secondes. Le délai maximal pour que ce problème était de 3 secondes pour leur donné des cas de test et j'ai été faire une erreur de dépassement de délai avant, donc je pense que cette optimisation est plutôt bonne.
Je n'ai aucune idée de la complexité du temps, mais je suis absolument certain de ce code peut être amélioré grâce à memoization depuis il y a 10 compte tenu des cas de test et les calculs pour les précédents cas de test peut être utilisé pour "tricher" sur l'avenir de cas de test. Je vais faire cette optimisation dans l'avenir, mais j'espère que cette réponse dans son état actuel permettra à quiconque de tenter ce problème à l'avenir, car elle évite les naïfs factorielle-la complexité de l'approche de la production et de l'itération sur toutes les permutations.
Si il y a une dynamique de programmation de solution, il n'y a sans doute une façon de le faire étape par étape, en utilisant les résultats des permutations de taille n pour aider avec les résultats obtenus pour les permutations de taille n+1.
Donné une permutation de longueur n - 1-n, vous pouvez obtenir une permutation de longueur n+1 en ajoutant de la valeur (n+1) n+1 positions possibles. (n+1) est supérieure à un de 1-n de sorte que le nombre d'inversions que vous créez lorsque vous faites cela dépend de l'endroit où vous l'ajouter - ajouter à la dernière position, et vous créez pas d'inversions, l'ajouter à la dernière position et de vous créer une inversion, et regarde en arrière à la n=4 cas avec une inversion de vérifier cela.
Donc, si vous pensez à l'une des n+1 des endroits où vous pouvez ajouter (n+1) si vous l'ajoutez à la place j en comptant à partir de la droite de sorte que la dernière position que la position 0, le nombre de permutations avec K inversions cela crée est le nombre de permutations avec K-j inversions sur les n les endroits.
Donc, si à chaque étape vous permet de compter le nombre de permutations avec K inversions pour tout K, vous pouvez mettre à jour le nombre de permutations avec K inversions de longueur n+1, en utilisant le nombre de permutations avec K inversions de longueur n.
Un problème majeur dans le calcul de ces coefficients est la taille de la commande du produit résultant. Le polynôme Produit i=1,2,..,n {(1+x).(1+x+x^2)....(1+x+x^2+..+x^i)+...(1+x+x^2+...+x^n), aura un ordre équivalent à n*(n+1). Par conséquent, cela met un restrictive de calcul de limite sur le processus. Si nous utilisons un processus où les résultats précédents pour le Produit de n-1 sont utilisés dans le processus de calcul du Produit de n, nous sommes à la recherche à la de stockage de (n-1)*n entiers. Il est possible d'utiliser un processus récursif, ce qui sera beaucoup plus lente, et encore, il est limité aux entiers inférieurs à la racine carrée de la commune de la taille de l'entier. La suite est un peu rugueuse et prêt récursive code pour ce problème. La fonction mahonian(r,c) retourne le c th coefficient pour la r th Produit. Mais là encore, c'est extrêmement lent pour les grands Produits de plus de 100. L'exécution de ce qu'il peut être vu que la récursivité est clairement pas la réponse.
Comme une question d'intérêt, j'ai inclus le suivant qui est une version c++ de Sashank qui est beaucoup plus rapide que mon exemple de récursivité. Remarque-je utiliser le tatou de la bibliothèque.
Nous pouvons faire usage de la programmation dynamique pour résoudre ce problème. nous avons n place pour remplir avec des chiffres de 1 à n, _ _ _ _ _ _ _ prendre n=7, alors à très premier lieu, nous pouvons atteindre antérieure au plus n-1 inversion et d'au moins 0 , de même pour la deuxième place, nous pouvons réaliser antérieure au plus n-2 inversion et d'au moins 0, en général, nous pouvons réaliser antérieure au plus n-i inversions à ith indice, quel que soit le choix de nous placer avant.
notre formule récursive ressemblera :
f(n,k) = f(n-1,k) + f(n-1,k-1) + f(n-1,k-2) ............. f(n-1,max(0,k-(n-1))
pas d'inversion d'une inversion de deux inversion n-1 inversion
nous pouvons atteindre 0 inversions en plaçant la plus petite de la quantité restante à partir de l'ensemble (1,n)
1 l'inversion, en plaçant deuxième plus petit et ainsi de suite,
condition de base pour notre formule récursive sera.
if( i==0 && k==0 ) return 1(valide permutation)
if( i==0 && k!=0 ) return 0 (non valide permutation).
si nous traçons la récursivité de l'arbre, nous allons voir les sous-problèmes répété plusieurs fois, Donc à utiliser memoization pour réduire la complexité de O(n*k).