Voici donc la solution O (n log n) en java.
long merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, count = 0;
while (i < left.length || j < right.length) {
if (i == left.length) {
arr[i+j] = right[j];
j++;
} else if (j == right.length) {
arr[i+j] = left[i];
i++;
} else if (left[i] <= right[j]) {
arr[i+j] = left[i];
i++;
} else {
arr[i+j] = right[j];
count += left.length-i;
j++;
}
}
return count;
}
long invCount(int[] arr) {
if (arr.length < 2)
return 0;
int m = (arr.length + 1) / 2;
int left[] = Arrays.copyOfRange(arr, 0, m);
int right[] = Arrays.copyOfRange(arr, m, arr.length);
return invCount(left) + invCount(right) + merge(arr, left, right);
}
C'est un tri de fusion presque normal, toute la magie est cachée dans la fonction de fusion. Notez que lors du tri, l'algorithme supprime les inversions. Alors que l'algorithme de fusion compte le nombre d'inversions supprimées (triées pourrait-on dire).
Le seul moment où les inversions sont supprimées est lorsque l'algorithme prend l'élément du côté droit d'un tableau et le fusionne avec le tableau principal. Le nombre d'inversions supprimées par cette opération est le nombre d'éléments restants du tableau de gauche à fusionner. :)
J'espère que c'est assez explicatif.
Marek Kirejczyk
la source
left.length - i
au compteur d'inversion? Je pense qu'il serait logique d'ajouter simplement 1, car vous êtes tombé dans le cas logique où la comparaison entre les deux sous-tableaux a un élément de tableau gauche plus grand que celui de droite. N'importe qui peut me l'expliquer comme si j'avais 5 ans?arr
. Mais ce n'est pas une inversion. Vous avez trouvé des inversions pour tous les éléments du tableau de gauche qui sont supérieurs à 6. Dans notre cas, il comprend également 8. Donc, 2 est ajouté àcount
, ce qui est égal àleft.length - i
.Je l'ai trouvé dans le temps O (n * log n) par la méthode suivante.
Prenez A [1] et trouvez sa position dans le tableau trié B via une recherche binaire. Le nombre d'inversions pour cet élément sera un de moins que le numéro d'index de sa position dans B puisque chaque nombre inférieur qui apparaîtra après le premier élément de A sera une inversion.
2a. accumuler le nombre d'inversions pour contrer la variable num_inversions.
2b. supprimer A [1] du tableau A et aussi de sa position correspondante dans le tableau B
Voici un exemple d'exécution de cet algorithme. Tableau d'origine A = (6, 9, 1, 14, 8, 12, 3, 2)
1: Fusionner le tri et copier dans le tableau B
B = (1, 2, 3, 6, 8, 9, 12, 14)
2: Prenez A [1] et une recherche binaire pour le trouver dans le tableau B
A [1] = 6
B = (1, 2, 3, 6 , 8, 9, 12, 14)
6 est à la 4ème position du tableau B, il y a donc 3 inversions. Nous le savons parce que 6 était en première position dans le tableau A, donc tout élément de valeur inférieure qui apparaîtra ultérieurement dans le tableau A aurait un indice de j> i (puisque i dans ce cas est 1).
2.b: Supprimez A [1] du tableau A et également de sa position correspondante dans le tableau B (les éléments en gras sont supprimés).
A = ( 6, 9, 1, 14, 8, 12, 3, 2) = (9, 1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 6, 8, 9, 12, 14) = (1, 2, 3, 8, 9, 12, 14)
3: Réexécutez à partir de l'étape 2 sur les nouvelles baies A et B.
A [1] = 9
B = (1, 2, 3, 8, 9, 12, 14)
9 est maintenant en 5ème position du tableau B, il y a donc 4 inversions. Nous le savons parce que 9 était en première position dans le tableau A, donc tout élément de valeur inférieure qui apparaîtrait ultérieurement aurait un indice de j> i (puisque i dans ce cas est à nouveau 1). Supprimer A [1] du tableau A et également de sa position correspondante dans le tableau B (les éléments en gras sont supprimés)
A = ( 9 , 1, 14, 8, 12, 3, 2) = (1, 14, 8, 12, 3, 2)
B = (1, 2, 3, 8, 9 , 12, 14) = (1, 2, 3, 8, 12, 14)
Continuer dans cette veine nous donnera le nombre total d'inversions pour le tableau A une fois la boucle terminée.
L'étape 1 (tri par fusion) nécessiterait O (n * log n) pour s'exécuter. L'étape 2 s'exécuterait n fois et à chaque exécution effectuerait une recherche binaire qui prend O (log n) pour s'exécuter pour un total de O (n * log n). La durée totale de fonctionnement serait donc O (n * log n) + O (n * log n) = O (n * log n).
Merci de votre aide. Écrire les exemples de tableaux sur un morceau de papier a vraiment aidé à visualiser le problème.
la source
En Python
la source
Je me demande pourquoi personne n'a encore mentionné les arbres à index binaire . Vous pouvez en utiliser un pour conserver les sommes de préfixes sur les valeurs de vos éléments de permutation. Ensuite, vous pouvez simplement procéder de droite à gauche et compter pour chaque élément le nombre d'éléments plus petit que celui de droite:
La complexité est O (n log n) et le facteur constant est très faible.
la source
i -= i & -i
ligne? Et de mêmei += i & -i
timeit
compare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.J'avais une question similaire à celle-ci pour les devoirs en fait. J'ai été limité qu'il doit avoir une efficacité O (nlogn).
J'ai utilisé l'idée que vous avez proposée d'utiliser Mergesort, car il est déjà d'une efficacité correcte. Je viens d'insérer du code dans la fonction de fusion qui était essentiellement: chaque fois qu'un nombre du tableau de droite est ajouté au tableau de sortie, j'ajoute au nombre total d'inversions, la quantité de nombres restant dans le tableau de gauche.
Cela a beaucoup de sens pour moi maintenant que j'y ai suffisamment réfléchi. Vous comptez combien de fois il y a un plus grand nombre avant les nombres.
hth.
la source
Le but principal de cette réponse est de comparer les vitesses des différentes versions de Python trouvées ici, mais j'ai aussi quelques contributions personnelles. (FWIW, je viens de découvrir cette question en effectuant une recherche en double).
Les vitesses d'exécution relatives des algorithmes implémentés dans CPython peuvent être différentes de ce à quoi on pourrait s'attendre d'une simple analyse des algorithmes et de l'expérience avec d'autres langages. C'est parce que Python fournit de nombreuses fonctions et méthodes puissantes implémentées en C qui peuvent fonctionner sur des listes et d'autres collections à une vitesse proche de celle obtenue dans un langage entièrement compilé, de sorte que ces opérations s'exécutent beaucoup plus rapidement que les algorithmes équivalents implémentés «manuellement» avec Python code.
Le code qui tire parti de ces outils peut souvent surpasser les algorithmes théoriquement supérieurs qui tentent de tout faire avec des opérations Python sur des éléments individuels de la collection. Bien entendu, la quantité réelle de données traitées a également un impact sur cela. Mais pour des quantités modérées de données, le code qui utilise un algorithme O (n²) fonctionnant à la vitesse C peut facilement battre un algorithme O (n log n) qui effectue l'essentiel de son travail avec des opérations Python individuelles.
La plupart des réponses publiées à cette question de comptage d'inversion utilisent un algorithme basé sur le tri par fusion. Théoriquement, c'est une bonne approche, sauf si la taille du tableau est très petite. Mais TimSort intégré à Python (un algorithme de tri hybride stable, dérivé du tri par fusion et du tri par insertion) fonctionne à la vitesse C, et un tri de fusion codé à la main en Python ne peut pas espérer rivaliser avec lui pour la vitesse.
L'une des solutions les plus intrigantes ici, dans la réponse publiée par Niklas B , utilise le tri intégré pour déterminer le classement des éléments du tableau, et un arbre indexé binaire (alias arbre Fenwick) pour stocker les sommes cumulées nécessaires pour calculer l'inversion compter. En essayant de comprendre cette structure de données et l'algorithme de Niklas, j'ai écrit quelques variantes de mes propres (postées ci-dessous). Mais j'ai également découvert que pour les tailles de liste modérées, il est en fait plus rapide d'utiliser la fonction intégrée de Python
sum
que le charmant arbre Fenwick.Finalement, lorsque la taille de la liste atteint environ 500, l'aspect O (n²) de l'appel
sum
à l' intérieur de cettefor
boucle fait son apparition et la performance commence à chuter.Mergesort n'est pas le seul tri O (nlogn), et plusieurs autres peuvent être utilisés pour effectuer le comptage d'inversion. La réponse de prasadvk utilise un tri d'arbre binaire, mais son code semble être en C ++ ou l'un de ses dérivés. J'ai donc ajouté une version Python. J'ai utilisé à l'origine une classe pour implémenter les nœuds d'arbre, mais j'ai découvert qu'un dict est nettement plus rapide. J'ai finalement utilisé list, ce qui est encore plus rapide, même si cela rend le code un peu moins lisible.
Un avantage de treeort est qu'il est beaucoup plus facile à implémenter de manière itérative que mergesort. Python n'optimise pas la récursivité et il a une limite de profondeur de récursivité (bien que cela puisse être augmenté si vous en avez vraiment besoin). Et bien sûr, les appels de fonction Python sont relativement lents, donc lorsque vous essayez d'optimiser la vitesse, il est bon d'éviter les appels de fonction, lorsque cela est possible.
Une autre sorte O (nlogn) est la vénérable sorte de base. Son gros avantage est qu'il ne compare pas les clés les unes aux autres. Son inconvénient est que cela fonctionne mieux sur des séquences contiguës d'entiers, idéalement une permutation d'entiers dans
range(b**m)
oùb
est généralement 2. J'ai ajouté quelques versions basées sur le tri par base après avoir tenté de lire Counting Inversions, Offline Orthogonal Range Counting, and Related Problems qui est liées au calcul du nombre d '«inversions» dans une permutation .Pour utiliser efficacement le tri par base pour compter les inversions dans une séquence générale
seq
de longueur n, nous pouvons créer une permutation derange(n)
qui a le même nombre d'inversions queseq
. Nous pouvons le faire dans (au pire) temps O (nlogn) via TimSort. L'astuce consiste à permuter les indices deseq
par triseq
. Il est plus facile d'expliquer cela avec un petit exemple.production
En triant les paires (valeur, index) de,
seq
nous avons permuté les indices deseq
avec le même nombre de swaps qui sont nécessaires pour mettreseq
dans son ordre d'origine à partir de son ordre trié. Nous pouvons créer cette permutation en triantrange(n)
avec une fonction clé appropriée:production
Nous pouvons éviter cela
lambda
en utilisantseq
la.__getitem__
méthode de:Ce n'est que légèrement plus rapide, mais nous recherchons toutes les améliorations de vitesse que nous pouvons obtenir. ;)
Le code ci-dessous effectue des
timeit
tests sur tous les algorithmes Python existants sur cette page, plus quelques-uns des miens: quelques versions de force brute O (n²), quelques variantes de l'algorithme de Niklas B, et bien sûr une basée sur le tri par fusion (que j'ai écrit sans me référer aux réponses existantes). Il a également mon code de tri d'arbres basé sur une liste dérivé grossièrement du code de prasadvk, et diverses fonctions basées sur le tri par base, certaines utilisant une stratégie similaire aux approches de tri par fusion, et d'autres utilisantsum
ou un arbre Fenwick.Ce programme mesure le temps d'exécution de chaque fonction sur une série de listes aléatoires d'entiers; il peut également vérifier que chaque fonction donne les mêmes résultats que les autres, et qu'elle ne modifie pas la liste d'entrée.
Chaque
timeit
appel donne un vecteur contenant 3 résultats, que je trie. La valeur principale à examiner ici est la valeur minimale, les autres valeurs donnent simplement une indication de la fiabilité de cette valeur minimale, comme indiqué dans la note de latimeit
documentation du module .Malheureusement, la sortie de ce programme est trop volumineuse pour être incluse dans cette réponse, donc je la poste dans sa propre réponse (wiki de la communauté) .
La sortie provient de 3 exécutions sur mon ancienne machine 32 bits monocœur 2 GHz exécutant Python 3.6.0 sur une ancienne distribution dérivée de Debian. YMMV. Pendant les tests, j'ai arrêté mon navigateur Web et déconnecté de mon routeur pour minimiser l'impact des autres tâches sur le processeur.
La première exécution teste toutes les fonctions avec des tailles de liste de 5 à 320, avec des tailles de boucle de 4096 à 64 (lorsque la taille de la liste double, la taille de la boucle est divisée par deux). Le pool aléatoire utilisé pour construire chaque liste est la moitié de la taille de la liste elle-même, donc nous sommes susceptibles d'obtenir beaucoup de doublons. Certains des algorithmes de comptage d'inversion sont plus sensibles aux doublons que d'autres.
La deuxième exécution utilise des listes plus grandes: 640 à 10240 et une taille de boucle fixe de 8. Pour gagner du temps, elle élimine plusieurs des fonctions les plus lentes des tests. Mes fonctions force brute O (n²) sont juste façon trop lent à ces dimensions, et comme mentionné plus haut, mon code qui utilise
sum
, ce qui fait si bien sur les petites listes modérées, ne peut pas rester sur les grandes listes.L'exécution finale couvre les tailles de liste de 20480 à 655360, et une taille de boucle fixe de 4, avec les 8 fonctions les plus rapides. Pour les tailles de liste inférieures à 40 000 ou plus, le code de Tim Babych est clairement le gagnant. Bravo Tim! Le code de Niklas B est également très performant, bien qu'il soit battu sur les petites listes. Le code basé sur la bissection de "python" fonctionne également plutôt bien, bien qu'il semble être un peu plus lent avec d'énormes listes avec beaucoup de doublons, probablement à cause de la
while
boucle linéaire qu'il utilise pour franchir les dupes.Cependant, pour les très grandes tailles de liste, les algorithmes basés sur les bissections ne peuvent pas rivaliser avec les vrais algorithmes O (nlogn).
Veuillez voir ici pour la sortie
la source
bisect
c'est C? Je suis presque sûr que c'est Python.Le nombre d'inversions peut être trouvé en analysant le processus de fusion dans le tri de fusion:
Lors de la copie d'un élément du deuxième tableau vers le tableau de fusion (le 9 dans cet exemple), il garde sa place par rapport aux autres éléments. Lors de la copie d'un élément du premier tableau vers le tableau de fusion (le 5 ici), il est inversé avec tous les éléments restant dans le deuxième tableau (2 inversions avec le 3 et le 4). Donc, une petite modification du tri par fusion peut résoudre le problème en O (n ln n).
Par exemple, décommentez simplement les deux lignes # dans le code python de fusion ci-dessous pour avoir le nombre.
MODIFIER 1
La même tâche peut être réalisée avec une version stable de tri rapide, connue pour être légèrement plus rapide:
En choisissant le pivot comme dernier élément, les inversions sont bien comptées et le temps d'exécution est 40% meilleur que celui de la fusion ci-dessus.
MODIFIER 2
Pour des performances en python, une version numpy et numba:
D'abord la partie numpy, qui utilise argsort O (n ln n):
Et la partie numba pour l' approche efficace BIT :
la source
timeit
compare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.timeit
collection.Notez que la réponse de Geoffrey Irving est fausse.
Prenons la séquence {3, 2, 1} comme exemple. Il y a trois inversions: (3, 2), (3, 1), (2, 1), donc le numéro d'inversion est 3. Cependant, selon la méthode citée, la réponse aurait été 2.
la source
Vérifiez ceci: http://www.cs.jhu.edu/~xfliu/600.363_F03/hw_solution/solution1.pdf
J'espère que cela vous donnera la bonne réponse.
la source
Voici une solution possible avec variation d'arbre binaire. Il ajoute un champ appelé rightSubTreeSize à chaque nœud d'arbre. Continuez à insérer des nombres dans l'arborescence binaire dans l'ordre dans lequel ils apparaissent dans le tableau. Si le nombre va à lhs du nœud, le nombre d'inversion pour cet élément serait (1 + rightSubTreeSize). Étant donné que tous ces éléments sont supérieurs à l'élément actuel et qu'ils seraient apparus plus tôt dans le tableau. Si l'élément va à rhs d'un nœud, augmentez simplement son rightSubTreeSize. Voici le code.
la source
if(p->data < q->data)
sinon les doublons ne sont pas gérés correctement. Et il n'est pas nécessaire de testerq
en haut de la boucle, unewhile
boucle inconditionnelle fonctionne très bien. De plus, vous avez négligé de mentionner de quelle langue il s'agit. :) Et votre fonction semble avoir perdu sa ligne d'en-tête.la source
Comme il s'agit d'une vieille question, je vais fournir ma réponse en C.
la source
Voici la solution c ++
la source
Cette réponse contient les résultats des
timeit
tests produits par le code dans ma réponse principale . Veuillez consulter cette réponse pour plus de détails!la source
Voici un code C pour les inversions de comptage
Une explication a été donnée en détail ici: http://www.geeksforgeeks.org/counting-inversions/
la source
O (n log n) temps, solution d'espace O (n) en java.
Un tri de fusion, avec un ajustement pour préserver le nombre d'inversions effectuées lors de l'étape de fusion. (pour une fusion bien expliquée, jetez un œil à http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html )
Puisque le tri par fusion peut être effectué sur place, la complexité de l'espace peut être améliorée à O (1).
Lors de l'utilisation de ce tri, les inversions se produisent uniquement lors de l'étape de fusion et uniquement lorsque nous devons placer un élément de la deuxième partie avant les éléments de la première moitié, par exemple
fusionné avec
nous avons 3 + 2 + 0 = 5 inversions:
Après avoir effectué les 5 inversions, notre nouvelle liste fusionnée est 0, 1, 5, 6, 10, 15, 22
Il existe une tâche de démonstration sur Codility appelée ArrayInversionCount, où vous pouvez tester votre solution.
la source
Voici l'implémentation de O (n * log (n)) perl:
la source
Ma réponse en Python:
1- Triez d'abord le tableau et faites-en une copie. Dans mon programme, B représente le tableau trié. 2- Itérer sur le tableau d'origine (non trié), et trouver l'index de cet élément sur la liste triée. Notez également l'index de l'élément. 3- Assurez-vous que l'élément n'a pas de doublons, si c'est le cas, vous devez changer la valeur de votre index par -1. La condition while dans mon programme fait exactement cela. 4- Continuez à compter l'inversion qui fera votre valeur d'index, et supprimez l'élément une fois que vous avez calculé son inversion.
la source
timeit
compare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.Eh bien, j'ai une solution différente, mais j'ai peur que cela ne fonctionne que pour des éléments de tableau distincts.
Pour expliquer mon code, nous continuons d'ajouter des éléments à partir de la fin de Array.Pour tout élément de tableau entrant, nous trouvons l' index du premier élément dans le vecteur v qui est supérieur à notre élément entrant et attribuons cette valeur au compte d'inversion de l'index de l'élément entrant Après cela, nous insérons cet élément dans le vecteur v à sa position correcte de sorte que le vecteur v reste dans l'ordre trié.
la source
Une autre solution Python, courte. Utilise le module bisect intégré, qui fournit des fonctions pour insérer l'élément à sa place dans un tableau trié et pour trouver l'index de l'élément dans un tableau trié.
L'idée est de stocker les éléments à gauche du n-ième dans un tel tableau, ce qui nous permettrait de trouver facilement le nombre d'entre eux supérieur au n-ième.
la source
timeit
compare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage. : DLa réponse facile O (n ^ 2) est d'utiliser des boucles for imbriquées et d'incrémenter un compteur pour chaque inversion
Maintenant, je suppose que vous voulez une solution plus efficace, je vais y réfléchir.
la source
Une solution possible en C ++ satisfaisant l'exigence de complexité en temps O (N * log (N)) serait la suivante.
Il diffère d'un tri par fusion régulier uniquement par le compteur.
la source
Voici ma solution O (n log n) dans Ruby:
Et quelques cas de test:
la source
La meilleure façon optimisée sera de le résoudre par le tri par fusion où se fusionnera nous pourrons vérifier combien d'inversions sont nécessaires en comparant les tableaux de gauche et de droite. Chaque fois que l'élément du tableau de gauche est supérieur à l'élément du tableau de droite, ce sera l'inversion.
Approche du tri par fusion: -
Voici le code. Le code est exactement le même que le tri par fusion, sauf l'extrait de code sous
mergeToParent
méthode où je compte l'inversion sous la condition else de(left[leftunPicked] < right[rightunPicked])
Une autre approche où l'on peut comparer le tableau d'entrée avec le tableau trié: - Cette implémentation de la réponse Diablo. Bien que cela ne devrait pas être une approche préférée, car la suppression des n éléments d'un tableau ou d'une liste est log (n ^ 2).
la source
Le nombre maximum d'inversions possibles pour une liste de taille
n
pourrait être généralisé par une expression:Donc, pour un tableau de taille
6
maximale, les inversions possibles seront égales15
.Pour atteindre une complexité de
n logn
nous pourrions utiliser l'algorithme d'inversion sur le tri par fusion.Voici les étapes généralisées:
inversionCount += leftSubArray.length
C'est tout!
Ceci est un exemple simple, j'ai fait en utilisant Javascript:
la source
Implémentation du comptage des inversions dans un tableau avec tri par fusion dans Swift:
Notez que le nombre de swaps est incrémenté de
(qui est la longueur relative du côté gauche du tableau moins l'index de l'élément courant sur le côté gauche)
... parce que c'est le nombre d'éléments que l'élément à droite du tableau a dû sauter (nombre d'inversions) pour être trié.
la source
La plupart des réponses sont basées sur,
MergeSort
mais ce n'est pas la seule façon de résoudre ce problème.O(nlogn)
Je vais discuter de quelques approches.
Utiliser un
Balanced Binary Search Tree
Quelque chose comme ça.
Binary Indexed Tree
Segment Tree
[0, a[i]-1]
et mise à joura[i] with 1
Aussi, lors de l'utilisation
BIT
ouSegment-Tree
une bonne idée est de faireCoordinate compression
la source
C ++ Θ (n lg n) Solution à l'impression de paires qui constituent en comptage d'inversion.
la source
Utilisez mergesort, dans l'étape de fusion, incrémentez le compteur si le nombre copié en sortie provient du tableau de droite.
la source
J'ai récemment dû faire ça en R:
la source