Comptage des inversions dans un tableau

108

Je conçois un algorithme pour faire ce qui suit: un tableau donné A[1... n], pour chaque i < j, trouve toutes les paires d'inversion telles que A[i] > A[j]. J'utilise le tri par fusion et je copie le tableau A vers le tableau B, puis je compare les deux tableaux, mais j'ai du mal à voir comment je peux utiliser cela pour trouver le nombre d'inversions. Tout conseil ou aide serait grandement apprécié.

el diablo
la source

Réponses:

139

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
2
J'ai essayé d'exécuter ceci et je n'ai pas obtenu la bonne réponse. Êtes-vous censé appeler invCount (intArray) dans main pour commencer? Avec intArray étant le tableau non trié des int? Je l'ai exécuté avec un tableau de nombreux entiers et j'ai obtenu -1887062008 comme réponse. Qu'est-ce que je fais mal?
Nearpoint
4
+1, voir une solution similaire en C ++ 11 , y compris une solution générale basée sur un itérateur et un échantillon de banc d'essai aléatoire utilisant des séquences de 5 à 25 éléments. Prendre plaisir!.
WhozCraig
3
Ce n'est pas une solution. J'ai essayé de l'exécuter et cela donne des résultats incorrects.
mirgee
2
Désolé pour la question newbish, mais que se passe-t-il avec l'ajout left.length - iau 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?
Alfredo Gallegos
2
@AlfredoGallegos, une brève illustration de la réponse de Marek. Considérons deux tableaux: [6, 8] et [4, 5]. Lorsque vous voyez que 6 est supérieur à 4, vous prenez 4 et placez-le 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.
ilya
86

Je l'ai trouvé dans le temps O (n * log n) par la méthode suivante.

  1. Fusionner le tableau de tri A et créer une copie (tableau B)
  2. 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

  3. recommencez à partir de l'étape 2 jusqu'à ce qu'il n'y ait plus d'éléments dans A.

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.

el diablo
la source
1
pourquoi utiliser le tri par fusion et non le tri rapide?
Alcott
5
@Alcott Quick sort a le pire temps d'exécution de O (n ^ 2), lorsque la liste est déjà triée et que le premier pivot est choisi à chaque tour. Le pire des cas du tri de fusion est O (n log n)
user482594
29
L'étape de suppression d'un tableau standard rend votre algorithme O (n ^ 2), en raison du décalage des valeurs. (C'est pourquoi le tri par insertion est O (n ^ 2))
Kyle Butt
commencer par le premier élément du tableau B et compter les éléments qui le précèdent dans le tableau A donnerait également le même résultat, à condition que vous les éliminiez comme vous l'avez décrit dans votre réponse.
tutak le
@el diablo Comment supprimer des éléments pour éviter la complexité n ^ 2 ??
Jerky
26

En Python

# O(n log n)

def count_inversion(lst):
    return merge_count_inversion(lst)[1]

def merge_count_inversion(lst):
    if len(lst) <= 1:
        return lst, 0
    middle = int( len(lst) / 2 )
    left, a = merge_count_inversion(lst[:middle])
    right, b = merge_count_inversion(lst[middle:])
    result, c = merge_count_split_inversion(left, right)
    return result, (a + b + c)

def merge_count_split_inversion(left, right):
    result = []
    count = 0
    i, j = 0, 0
    left_len = len(left)
    while i < left_len and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            count += left_len - i
            j += 1
    result += left[i:]
    result += right[j:]
    return result, count        


#test code
input_array_1 = []  #0
input_array_2 = [1] #0
input_array_3 = [1, 5]  #0
input_array_4 = [4, 1] #1
input_array_5 = [4, 1, 2, 3, 9] #3
input_array_6 = [4, 1, 3, 2, 9, 5]  #5
input_array_7 = [4, 1, 3, 2, 9, 1]  #8

print count_inversion(input_array_1)
print count_inversion(input_array_2)
print count_inversion(input_array_3)
print count_inversion(input_array_4)
print count_inversion(input_array_5)
print count_inversion(input_array_6)
print count_inversion(input_array_7)
mkso
la source
13
Je suis déconcerté par la façon dont cela a réussi à atteindre +13 - je ne suis pas particulièrement doué en Python, mais cela semble à peu près identique à la version Java présentée 2 ans auparavant , sauf que cela ne fournit aucune explication . Publier des réponses dans toutes les autres langues est activement nuisible à l'OMI - il y a probablement des milliers, sinon beaucoup plus de langues - j'espère que personne ne soutiendra que nous devrions publier des milliers de réponses à une question - Stack Exchange n'a pas été fait pour cela .
Bernhard Barker
1
@tennenrishin D'accord, peut-être pas des milliers. Mais où pouvons-nous tracer la ligne? Il y a actuellement, comme je le compte, dix réponses qui donnent déjà la même approche . Cela représente environ 43% des réponses (sans la non-réponse) - un peu d'espace à prendre étant donné qu'il y a une demi-douzaine d'autres approches présentées ici. Même s'il n'y a que 2 réponses pour la même approche, cela dilue encore inutilement les réponses. Et j'ai fait un argument assez décent pour que cette réponse ne soit spécifiquement pas utile dans mon commentaire précédent.
Bernhard Barker
3
@Dukeling Comme vous, je ne suis pas familier avec Python, et plus familier avec Java. Je trouve cette solution beaucoup moins lisible que celle de Java. Il va de soi alors que pour certaines personnes, l'inverse pourrait être vrai dans la même mesure.
Museful
3
Pour une grande majorité d'utilisateurs, python est proche du code sudo. Je trouve honnêtement cela beaucoup plus lisible que celui de java même s'il n'a aucune explication. Je ne vois pas la nécessité d'être si ennuyé si cela aide certains lecteurs.
Francisco Vargas
2
Cette solution est parfaitement adaptée et lisible pour les utilisateurs de python. Les gens veulent voir comment les autres l'ont implémenté en Python.
Aerin
24

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:

def count_inversions(a):
  res = 0
  counts = [0]*(len(a)+1)
  rank = { v : i+1 for i, v in enumerate(sorted(a)) }
  for x in reversed(a):
    i = rank[x] - 1
    while i:
      res += counts[i]
      i -= i & -i
    i = rank[x]
    while i <= len(a):
      counts[i] += 1
      i += i & -i
  return res

La complexité est O (n log n) et le facteur constant est très faible.

Niklas B.
la source
probablement la meilleure approche :)
Nilutpal Borgohain
@NilutpalBorgohain Merci :) Il semble exiger au moins le moins de code parmi les candidats O (n log n).
Niklas B.29
1
Merci pour cela. Quelle est la signification de la i -= i & -iligne? Et de mêmei += i & -i
Gerard Condon
1
@GerardCondon, c'est essentiellement la structure de données BIT. Un lien expliquant cela se trouve dans la réponse
Niklas B.
TIL sur les arbres Fenwick. Merci! J'ai publié une réponse qui timeitcompare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.
PM 2Ring
14

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
6
Je soutiens votre réponse, la différence essentielle par rapport au tri par fusion réside dans la fonction de fusion lorsque l'élément du 2ème tableau de droite est copié dans le tableau de sortie => compteur d'inversion d'incrémentation du nombre d'éléments restants dans le 1er tableau de gauche
Alex.Salnikov
11

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 sumque le charmant arbre Fenwick.

def count_inversions(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += sum(counts[:i])
        counts[i] += 1
    return total

Finalement, lorsque la taille de la liste atteint environ 500, l'aspect O (n²) de l'appel sumà l' intérieur de cette forboucle 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)best 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 seqde longueur n, nous pouvons créer une permutation de range(n)qui a le même nombre d'inversions que seq. Nous pouvons le faire dans (au pire) temps O (nlogn) via TimSort. L'astuce consiste à permuter les indices de seqpar tri seq. Il est plus facile d'expliquer cela avec un petit exemple.

seq = [15, 14, 11, 12, 10, 13]
b = [t[::-1] for t in enumerate(seq)]
print(b)
b.sort()
print(b)

production

[(15, 0), (14, 1), (11, 2), (12, 3), (10, 4), (13, 5)]
[(10, 4), (11, 2), (12, 3), (13, 5), (14, 1), (15, 0)]

En triant les paires (valeur, index) de, seqnous avons permuté les indices de seqavec le même nombre de swaps qui sont nécessaires pour mettre seqdans son ordre d'origine à partir de son ordre trié. Nous pouvons créer cette permutation en triant range(n)avec une fonction clé appropriée:

print(sorted(range(len(seq)), key=lambda k: seq[k]))

production

[4, 2, 3, 5, 1, 0]

Nous pouvons éviter cela lambdaen utilisant seqla .__getitem__méthode de:

sorted(range(len(seq)), key=seq.__getitem__)

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 timeittests 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 utilisant sumou 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 timeitappel 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 la timeitdocumentation 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 whileboucle 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).

#!/usr/bin/env python3

''' Test speeds of various ways of counting inversions in a list

    The inversion count is a measure of how sorted an array is.
    A pair of items in a are inverted if i < j but a[j] > a[i]

    See /programming/337664/counting-inversions-in-an-array

    This program contains code by the following authors:
    mkso
    Niklas B
    B. M.
    Tim Babych
    python
    Zhe Hu
    prasadvk
    noman pouigt
    PM 2Ring

    Timing and verification code by PM 2Ring
    Collated 2017.12.16
    Updated 2017.12.21
'''

from timeit import Timer
from random import seed, randrange
from bisect import bisect, insort_left

seed('A random seed string')

# Merge sort version by mkso
def count_inversion_mkso(lst):
    return merge_count_inversion(lst)[1]

def merge_count_inversion(lst):
    if len(lst) <= 1:
        return lst, 0
    middle = len(lst) // 2
    left, a = merge_count_inversion(lst[:middle])
    right, b = merge_count_inversion(lst[middle:])
    result, c = merge_count_split_inversion(left, right)
    return result, (a + b + c)

def merge_count_split_inversion(left, right):
    result = []
    count = 0
    i, j = 0, 0
    left_len = len(left)
    while i < left_len and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            count += left_len - i
            j += 1
    result += left[i:]
    result += right[j:]
    return result, count

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Using a Binary Indexed Tree, aka a Fenwick tree, by Niklas B.
def count_inversions_NiklasB(a):
    res = 0
    counts = [0] * (len(a) + 1)
    rank = {v: i for i, v in enumerate(sorted(a), 1)}
    for x in reversed(a):
        i = rank[x] - 1
        while i:
            res += counts[i]
            i -= i & -i
        i = rank[x]
        while i <= len(a):
            counts[i] += 1
            i += i & -i
    return res

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by B.M
# Modified by PM 2Ring to deal with the global counter
bm_count = 0

def merge_count_BM(seq):
    global bm_count
    bm_count = 0
    sort_bm(seq)
    return bm_count

def merge_bm(l1,l2):
    global bm_count
    l = []
    while l1 and l2:
        if l1[-1] <= l2[-1]:
            l.append(l2.pop())
        else:
            l.append(l1.pop())
            bm_count += len(l2)
    l.reverse()
    return l1 + l2 + l

def sort_bm(l):
    t = len(l) // 2
    return merge_bm(sort_bm(l[:t]), sort_bm(l[t:])) if t > 0 else l

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Bisection based method by Tim Babych
def solution_TimBabych(A):
    sorted_left = []
    res = 0
    for i in range(1, len(A)):
        insort_left(sorted_left, A[i-1])
        # i is also the length of sorted_left
        res += (i - bisect(sorted_left, A[i]))
    return res

# Slightly faster, except for very small lists
def solutionE_TimBabych(A):
    res = 0
    sorted_left = []
    for i, u in enumerate(A):
        # i is also the length of sorted_left
        res += (i - bisect(sorted_left, u))
        insort_left(sorted_left, u)
    return res

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Bisection based method by "python"
def solution_python(A):
    B = list(A)
    B.sort()
    inversion_count = 0
    for i in range(len(A)):
        j = binarySearch_python(B, A[i])
        while B[j] == B[j - 1]:
            if j < 1:
                break
            j -= 1
        inversion_count += j
        B.pop(j)
    return inversion_count

def binarySearch_python(alist, item):
    first = 0
    last = len(alist) - 1
    found = False
    while first <= last and not found:
        midpoint = (first + last) // 2
        if alist[midpoint] == item:
            return midpoint
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by Zhe Hu
def inv_cnt_ZheHu(a):
    _, count = inv_cnt(a.copy())
    return count

def inv_cnt(a):
    n = len(a)
    if n==1:
        return a, 0
    left = a[0:n//2] # should be smaller
    left, cnt1 = inv_cnt(left)
    right = a[n//2:] # should be larger
    right, cnt2 = inv_cnt(right)

    cnt = 0
    i_left = i_right = i_a = 0
    while i_a < n:
        if (i_right>=len(right)) or (i_left < len(left)
            and left[i_left] <= right[i_right]):
            a[i_a] = left[i_left]
            i_left += 1
        else:
            a[i_a] = right[i_right]
            i_right += 1
            if i_left < len(left):
                cnt += len(left) - i_left
        i_a += 1
    return (a, cnt1 + cnt2 + cnt)

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by noman pouigt
# From https://stackoverflow.com/q/47830098
def reversePairs_nomanpouigt(nums):
    def merge(left, right):
        if not left or not right:
            return (0, left + right)
        #if everything in left is less than right
        if left[len(left)-1] < right[0]:
            return (0, left + right)
        else:
            left_idx, right_idx, count = 0, 0, 0
            merged_output = []

            # check for condition before we merge it
            while left_idx < len(left) and right_idx < len(right):
                #if left[left_idx] > 2 * right[right_idx]:
                if left[left_idx] > right[right_idx]:
                    count += len(left) - left_idx
                    right_idx += 1
                else:
                    left_idx += 1

            #merging the sorted list
            left_idx, right_idx = 0, 0
            while left_idx < len(left) and right_idx < len(right):
                if left[left_idx] > right[right_idx]:
                    merged_output += [right[right_idx]]
                    right_idx += 1
                else:
                    merged_output += [left[left_idx]]
                    left_idx += 1
            if left_idx == len(left):
                merged_output += right[right_idx:]
            else:
                merged_output += left[left_idx:]
        return (count, merged_output)

    def partition(nums):
        count = 0
        if len(nums) == 1 or not nums:
            return (0, nums)
        pivot = len(nums)//2
        left_count, l = partition(nums[:pivot])
        right_count, r = partition(nums[pivot:])
        temp_count, temp_list = merge(l, r)
        return (temp_count + left_count + right_count, temp_list)
    return partition(nums)[0]

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# PM 2Ring
def merge_PM2R(seq):
    seq, count = merge_sort_count_PM2R(seq)
    return count

def merge_sort_count_PM2R(seq):
    mid = len(seq) // 2
    if mid == 0:
        return seq, 0
    left, left_total = merge_sort_count_PM2R(seq[:mid])
    right, right_total = merge_sort_count_PM2R(seq[mid:])
    total = left_total + right_total
    result = []
    i = j = 0
    left_len, right_len = len(left), len(right)
    while i < left_len and j < right_len:
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
            total += left_len - i
    result.extend(left[i:])
    result.extend(right[j:])
    return result, total

def rank_sum_PM2R(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += sum(counts[:i])
        counts[i] += 1
    return total

# Fenwick tree functions adapted from C code on Wikipedia
def fen_sum(tree, i):
    ''' Return the sum of the first i elements, 0 through i-1 '''
    total = 0
    while i:
        total += tree[i-1]
        i -= i & -i
    return total

def fen_add(tree, delta, i):
    ''' Add delta to element i and thus 
        to fen_sum(tree, j) for all j > i 
    '''
    size = len(tree)
    while i < size:
        tree[i] += delta
        i += (i+1) & -(i+1)

def fenwick_PM2R(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += fen_sum(counts, i)
        fen_add(counts, 1, i)
    return total

def fenwick_inline_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        j = i + 1
        while i:
            total += counts[i]
            i -= i & -i
        while j < size:
            counts[j] += 1
            j += j & -j
    return total

def bruteforce_loops_PM2R(a):
    total = 0
    for i in range(1, len(a)):
        u = a[i]
        for j in range(i):
            if a[j] > u:
                total += 1
    return total

def bruteforce_sum_PM2R(a):
    return sum(1 for i in range(1, len(a)) for j in range(i) if a[j] > a[i])

# Using binary tree counting, derived from C++ code (?) by prasadvk
# https://stackoverflow.com/a/16056139
def ltree_count_PM2R(a):
    total, root = 0, None
    for u in a:
        # Store data in a list-based tree structure
        # [data, count, left_child, right_child]
        p = [u, 0, None, None]
        if root is None:
            root = p
            continue
        q = root
        while True:
            if p[0] < q[0]:
                total += 1 + q[1]
                child = 2
            else:
                q[1] += 1
                child = 3
            if q[child]:
                q = q[child]
            else:
                q[child] = p
                break
    return total

# Counting based on radix sort, recursive version
def radix_partition_rec(a, L):
    if len(a) < 2:
        return 0
    if len(a) == 2:
        return a[1] < a[0]
    left, right = [], []
    count = 0
    for u in a:
        if u & L:
            right.append(u)
        else:
            count += len(right)
            left.append(u)
    L >>= 1
    if L:
        count += radix_partition_rec(left, L) + radix_partition_rec(right, L)
    return count

# The following functions determine swaps using a permutation of 
# range(len(a)) that has the same inversion count as `a`. We can create
# this permutation with `sorted(range(len(a)), key=lambda k: a[k])`
# but `sorted(range(len(a)), key=a.__getitem__)` is a little faster.

# Counting based on radix sort, iterative version
def radix_partition_iter(seq, L):
    count = 0
    parts = [seq]
    while L and parts:
        newparts = []
        for a in parts:
            if len(a) < 2:
                continue
            if len(a) == 2:
                count += a[1] < a[0]
                continue
            left, right = [], []
            for u in a:
                if u & L:
                    right.append(u)
                else:
                    count += len(right)
                    left.append(u)
            if left:
                newparts.append(left)
            if right:
                newparts.append(right)
        parts = newparts
        L >>= 1
    return count

def perm_radixR_PM2R(a):
    size = len(a)
    b = sorted(range(size), key=a.__getitem__)
    n = size.bit_length() - 1
    return radix_partition_rec(b, 1 << n)

def perm_radixI_PM2R(a):
    size = len(a)
    b = sorted(range(size), key=a.__getitem__)
    n = size.bit_length() - 1
    return radix_partition_iter(b, 1 << n)

# Plain sum of the counts of the permutation
def perm_sum_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    for i in reversed(sorted(range(size), key=a.__getitem__)):
        total += sum(counts[:i])
        counts[i] = 1
    return total

# Fenwick sum of the counts of the permutation
def perm_fenwick_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    for i in reversed(sorted(range(size), key=a.__getitem__)):
        j = i + 1
        while i:
            total += counts[i]
            i -= i & -i
        while j < size:
            counts[j] += 1
            j += j & -j
    return total

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# All the inversion-counting functions
funcs = (
    solution_TimBabych,
    solutionE_TimBabych,
    solution_python,
    count_inversion_mkso,
    count_inversions_NiklasB,
    merge_count_BM,
    inv_cnt_ZheHu,
    reversePairs_nomanpouigt,
    fenwick_PM2R,
    fenwick_inline_PM2R,
    merge_PM2R,
    rank_sum_PM2R,
    bruteforce_loops_PM2R,
    bruteforce_sum_PM2R,
    ltree_count_PM2R,
    perm_radixR_PM2R,
    perm_radixI_PM2R,
    perm_sum_PM2R,
    perm_fenwick_PM2R,
)

def time_test(seq, loops, verify=False):
    orig = seq
    timings = []
    for func in funcs:
        seq = orig.copy()
        value = func(seq) if verify else None
        t = Timer(lambda: func(seq))
        result = sorted(t.repeat(3, loops))
        timings.append((result, func.__name__, value))
        assert seq==orig, 'Sequence altered by {}!'.format(func.__name__)
    first = timings[0][-1]
    timings.sort()
    for result, name, value in timings:
        result = ', '.join([format(u, '.5f') for u in result])
        print('{:24} : {}'.format(name, result))

    if verify:
        # Check that all results are identical
        bad = ['%s: %d' % (name, value)
            for _, name, value in timings if value != first]
        if bad:
            print('ERROR. Value: {}, bad: {}'.format(first, ', '.join(bad)))
        else:
            print('Value: {}'.format(first))
    print()

#Run the tests
size, loops = 5, 1 << 12
verify = True
for _ in range(7):
    hi = size // 2
    print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    seq = [randrange(hi) for _ in range(size)]
    time_test(seq, loops, verify)
    loops >>= 1
    size <<= 1

#size, loops = 640, 8
#verify = False
#for _ in range(5):
    #hi = size // 2
    #print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    #seq = [randrange(hi) for _ in range(size)]
    #time_test(seq, loops, verify)
    #size <<= 1

#size, loops = 163840, 4
#verify = False
#for _ in range(3):
    #hi = size // 2
    #print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    #seq = [randrange(hi) for _ in range(size)]
    #time_test(seq, loops, verify)
    #size <<= 1

Veuillez voir ici pour la sortie

PM 2Bague
la source
Merci, c'était assez amusant :) Montre clairement les avantages de l'utilisation du module C - ce qui est en deux.
Tim Babych
Le problème est que le gagnant utilise (théoriquement) un algorithme quadratique. pour une taille ~ 100 000, il sera battu par d'autres. J'ai édité mon article pour mettre une solution de vitesse C quasi linéaire en python.
BM
@BM Bien sûr, mais l'approche bissectrice de Tim est assez bonne jusqu'à ce que vous atteigniez une taille d'environ 45 000. J'ai quelques autres solutions que j'ajouterai ici dans les prochains jours.
PM 2 Ring
@TimBabych Vous dites que bisectc'est C? Je suis presque sûr que c'est Python.
Stefan Pochmann
10

Le nombre d'inversions peut être trouvé en analysant le processus de fusion dans le tri de fusion: processus 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.

def merge(l1,l2):
    l = []
    # global count
    while l1 and l2:
        if l1[-1] <= l2[-1]:
            l.append(l2.pop())
        else:
            l.append(l1.pop())
            # count += len(l2)
    l.reverse()
    return l1 + l2 + l

def sort(l): 
    t = len(l) // 2
    return merge(sort(l[:t]), sort(l[t:])) if t > 0 else l

count=0
print(sort([5,1,2,4,9,3]), count)
# [1, 2, 3, 4, 5, 9] 6

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:

def part(l):
    pivot=l[-1]
    small,big = [],[]
    count = big_count = 0
    for x in l:
        if x <= pivot:
            small.append(x)
            count += big_count
        else:
            big.append(x)
            big_count += 1
    return count,small,big

def quick_count(l):
    if len(l)<2 : return 0
    count,small,big = part(l)
    small.pop()
    return count + quick_count(small) + quick_count(big)

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):

def count_inversions(a):
    n = a.size
    counts = np.arange(n) & -np.arange(n)  # The BIT
    ags = a.argsort(kind='mergesort')    
    return  BIT(ags,counts,n)

Et la partie numba pour l' approche efficace BIT :

@numba.njit
def BIT(ags,counts,n):
    res = 0        
    for x in ags :
        i = x
        while i:
            res += counts[i]
            i -= i & -i
        i = x+1
        while i < n:
            counts[i] -= 1
            i += i & -i
    return  res  
BM
la source
J'ai publié une réponse qui timeitcompare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.
PM 2Ring
Aucun problème de performances dans ce post ... Je vais essayer dans un certain temps. Numpy numba autorisé;)?
BM
Je n'ai jamais utilisé Numba, mais j'ai un peu utilisé Numpy, et j'ai pensé à ajouter moi-même une version Numpy, mais j'ai décidé de limiter les tests aux solutions qui n'utilisent que la bibliothèque standard. Mais je suppose qu'il serait intéressant de voir comment une solution Numpy se compare. Je soupçonne que ce ne sera pas plus rapide sur les petites listes.
PM 2Ring
Une vitesse 100x est impressionnante! Mais je ne peux pas l'exécuter car je n'ai pas Numba. Et comme je l'ai dit plus tôt, il ne serait pas juste de l'inclure dans ma timeitcollection.
PM 2 Ring
8

Notez que la réponse de Geoffrey Irving est fausse.

Le nombre d'inversions dans un tableau correspond à la moitié de la distance totale que les éléments doivent être déplacés pour trier le tableau. Par conséquent, il peut être calculé en triant le tableau, en conservant la permutation résultante p [i], puis en calculant la somme de abs (p [i] -i) / 2. Cela prend O (n log n) temps, ce qui est optimal.

Une méthode alternative est donnée à http://mathworld.wolfram.com/PermutationInversion.html . Cette méthode équivaut à la somme de max (0, p [i] -i), qui est égale à la somme de abs (p [i] -i]) / 2 puisque la distance totale des éléments se déplaçant vers la gauche est égale à la les éléments de distance totale se déplacent vers la droite.

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.

user1465038
la source
La bonne réponse peut être trouvée en comptant le nombre minimum requis de swaps adjacents. Voir la discussion: stackoverflow.com/questions/20990127/…
Isaac Turner
4

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.

Node { 
    int data;
    Node* left, *right;
    int rightSubTreeSize;

    Node(int data) { 
        rightSubTreeSize = 0;
    }   
};

Node* root = null;
int totCnt = 0;
for(i = 0; i < n; ++i) { 
    Node* p = new Node(a[i]);
    if(root == null) { 
        root = p;
        continue;
    } 

    Node* q = root;
    int curCnt = 0;
    while(q) { 
        if(p->data <= q->data) { 
            curCnt += 1 + q->rightSubTreeSize;
            if(q->left) { 
                q = q->left;
            } else { 
                q->left = p;
                break;
            }
        } else { 
            q->rightSubTreeSize++;
            if(q->right) { 
                q = q->right;
            } else { 
                q->right = p;
                break;
            }
        }
    }

    totCnt += curCnt;
  }
  return totCnt;
prasadvk
la source
C'est une approche intéressante, et elle semble être assez rapide. Cependant, cette comparaison doit être effectuée, if(p->data < q->data)sinon les doublons ne sont pas gérés correctement. Et il n'est pas nécessaire de tester qen haut de la boucle, une whileboucle 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.
PM 2 Ring
Je viens d'ajouter une version Python basée sur votre algorithme d'arbre à ma réponse. Bien sûr, ce n'est pas aussi rapide qu'une version entièrement compilée, mais cela fonctionne plutôt bien, par rapport aux autres versions de Python.
PM 2Ring
3
public static int mergeSort(int[] a, int p, int r)
{
    int countInversion = 0;
    if(p < r)
    {
        int q = (p + r)/2;
        countInversion = mergeSort(a, p, q);
        countInversion += mergeSort(a, q+1, r);
        countInversion += merge(a, p, q, r);
    }
    return countInversion;
}

public static int merge(int[] a, int p, int q, int r)
{
    //p=0, q=1, r=3
    int countingInversion = 0;
    int n1 = q-p+1;
    int n2 = r-q;
    int[] temp1 = new int[n1+1];
    int[] temp2 = new int[n2+1];
    for(int i=0; i<n1; i++) temp1[i] = a[p+i];
    for(int i=0; i<n2; i++) temp2[i] = a[q+1+i];

    temp1[n1] = Integer.MAX_VALUE;
    temp2[n2] = Integer.MAX_VALUE;
    int i = 0, j = 0;

    for(int k=p; k<=r; k++)
    {
        if(temp1[i] <= temp2[j])
        {
            a[k] = temp1[i];
            i++;
        }
        else
        {
            a[k] = temp2[j];
            j++;
            countingInversion=countingInversion+(n1-i); 
        }
    }
    return countingInversion;
}
public static void main(String[] args)
{
    int[] a = {1, 20, 6, 4, 5};
    int countInversion = mergeSort(a, 0, a.length-1);
    System.out.println(countInversion);
}
En essayant
la source
3
Est-ce bien différent des solutions Java et Python déjà publiées? En outre, les réponses de code uniquement ne sont pas particulièrement bonnes IMO, d'autant plus que cette question n'a même pas spécifié de langue.
Bernhard Barker
2

Comme il s'agit d'une vieille question, je vais fournir ma réponse en C.

#include <stdio.h>

int count = 0;
int inversions(int a[], int len);
void mergesort(int a[], int left, int right);
void merge(int a[], int left, int mid, int right);

int main() {
  int a[] = { 1, 5, 2, 4, 0 };
  printf("%d\n", inversions(a, 5));
}

int inversions(int a[], int len) {
  mergesort(a, 0, len - 1);
  return count;
}

void mergesort(int a[], int left, int right) {
  if (left < right) {
     int mid = (left + right) / 2;
     mergesort(a, left, mid);
     mergesort(a, mid + 1, right);
     merge(a, left, mid, right);
  }
}

void merge(int a[], int left, int mid, int right) {
  int i = left;
  int j = mid + 1;
  int k = 0;
  int b[right - left + 1];
  while (i <= mid && j <= right) {
     if (a[i] <= a[j]) {
       b[k++] = a[i++];
     } else {
       printf("right element: %d\n", a[j]);
       count += (mid - i + 1);
       printf("new count: %d\n", count);
       b[k++] = a[j++];
     }
  }
  while (i <= mid)
    b[k++] = a[i++];
  while (j <= right)
    b[k++] = a[j++];
  for (i = left, k = 0; i <= right; i++, k++) {
    a[i] = b[k];
  }
}
métissage
la source
-1 parce qu'une réponse dans toutes les autres langues conduirait à un trop grand nombre de réponses désespérément, qui dupliquent essentiellement des informations déjà présentées dans d'autres réponses. De plus, il s'agit essentiellement d'une réponse de code uniquement sans explication, ce qui est, au mieux, principalement approprié pour les questions relatives à cette langue.
Bernhard Barker
2

Voici la solution c ++

/**
*array sorting needed to verify if first arrays n'th element is greater than sencond arrays
*some element then all elements following n will do the same
*/
#include<stdio.h>
#include<iostream>
using namespace std;
int countInversions(int array[],int size);
int merge(int arr1[],int size1,int arr2[],int size2,int[]);
int main()
{
    int array[] = {2, 4, 1, 3, 5};
    int size = sizeof(array) / sizeof(array[0]);
    int x = countInversions(array,size);
    printf("number of inversions = %d",x);
}

int countInversions(int array[],int size)
{
    if(size > 1 )
    {
    int mid = size / 2;
    int count1 = countInversions(array,mid);
    int count2 = countInversions(array+mid,size-mid);
    int temp[size];
    int count3 = merge(array,mid,array+mid,size-mid,temp);
    for(int x =0;x<size ;x++)
    {
        array[x] = temp[x];
    }
    return count1 + count2 + count3;
    }else{
        return 0;
    }
}

int merge(int arr1[],int size1,int arr2[],int size2,int temp[])
{
    int count  = 0;
    int a = 0;
    int b = 0;
    int c = 0;
    while(a < size1 && b < size2)
    {
        if(arr1[a] < arr2[b])
        {
            temp[c] = arr1[a];
            c++;
            a++;
        }else{
            temp[c] = arr2[b];
            b++;
            c++;
            count = count + size1 -a;
        }
    }

    while(a < size1)
    {
        temp[c] = arr1[a];
        c++;a++;
    }

while(b < size2)
    {
        temp[c] = arr2[b];
        c++;b++;
    }

    return count;
}
Dheeraj Sachan
la source
2

Cette réponse contient les résultats des timeittests produits par le code dans ma réponse principale . Veuillez consulter cette réponse pour plus de détails!

count_inversions speed test results

Size = 5, hi = 2, 4096 loops
ltree_count_PM2R         : 0.04871, 0.04872, 0.04876
bruteforce_loops_PM2R    : 0.05696, 0.05700, 0.05776
solution_TimBabych       : 0.05760, 0.05822, 0.05943
solutionE_TimBabych      : 0.06642, 0.06704, 0.06760
bruteforce_sum_PM2R      : 0.07523, 0.07545, 0.07563
perm_sum_PM2R            : 0.09873, 0.09875, 0.09935
rank_sum_PM2R            : 0.10449, 0.10463, 0.10468
solution_python          : 0.13034, 0.13061, 0.13221
fenwick_inline_PM2R      : 0.14323, 0.14610, 0.18802
perm_radixR_PM2R         : 0.15146, 0.15203, 0.15235
merge_count_BM           : 0.16179, 0.16267, 0.16467
perm_radixI_PM2R         : 0.16200, 0.16202, 0.16768
perm_fenwick_PM2R        : 0.16887, 0.16920, 0.17075
merge_PM2R               : 0.18262, 0.18271, 0.18418
count_inversions_NiklasB : 0.19183, 0.19279, 0.20388
count_inversion_mkso     : 0.20060, 0.20141, 0.20398
inv_cnt_ZheHu            : 0.20815, 0.20841, 0.20906
fenwick_PM2R             : 0.22109, 0.22137, 0.22379
reversePairs_nomanpouigt : 0.29620, 0.29689, 0.30293
Value: 5

Size = 10, hi = 5, 2048 loops
solution_TimBabych       : 0.05954, 0.05989, 0.05991
solutionE_TimBabych      : 0.05970, 0.05972, 0.05998
perm_sum_PM2R            : 0.07517, 0.07519, 0.07520
ltree_count_PM2R         : 0.07672, 0.07677, 0.07684
bruteforce_loops_PM2R    : 0.07719, 0.07724, 0.07817
rank_sum_PM2R            : 0.08587, 0.08823, 0.08864
bruteforce_sum_PM2R      : 0.09470, 0.09472, 0.09484
solution_python          : 0.13126, 0.13154, 0.13185
perm_radixR_PM2R         : 0.14239, 0.14320, 0.14474
perm_radixI_PM2R         : 0.14632, 0.14669, 0.14679
fenwick_inline_PM2R      : 0.16796, 0.16831, 0.17030
perm_fenwick_PM2R        : 0.18189, 0.18212, 0.18638
merge_count_BM           : 0.19816, 0.19870, 0.19948
count_inversions_NiklasB : 0.21807, 0.22031, 0.22215
merge_PM2R               : 0.22037, 0.22048, 0.26106
fenwick_PM2R             : 0.24290, 0.24314, 0.24744
count_inversion_mkso     : 0.24895, 0.24899, 0.25205
inv_cnt_ZheHu            : 0.26253, 0.26259, 0.26590
reversePairs_nomanpouigt : 0.35711, 0.35762, 0.35973
Value: 20

Size = 20, hi = 10, 1024 loops
solutionE_TimBabych      : 0.05687, 0.05696, 0.05720
solution_TimBabych       : 0.06126, 0.06151, 0.06168
perm_sum_PM2R            : 0.06875, 0.06906, 0.07054
rank_sum_PM2R            : 0.07988, 0.07995, 0.08002
ltree_count_PM2R         : 0.11232, 0.11239, 0.11257
bruteforce_loops_PM2R    : 0.12553, 0.12584, 0.12592
solution_python          : 0.13472, 0.13540, 0.13694
bruteforce_sum_PM2R      : 0.15820, 0.15849, 0.16021
perm_radixI_PM2R         : 0.17101, 0.17148, 0.17229
perm_radixR_PM2R         : 0.17891, 0.18087, 0.18366
perm_fenwick_PM2R        : 0.20554, 0.20708, 0.21412
fenwick_inline_PM2R      : 0.21161, 0.21163, 0.22047
merge_count_BM           : 0.24125, 0.24261, 0.24565
count_inversions_NiklasB : 0.25712, 0.25754, 0.25778
merge_PM2R               : 0.26477, 0.26566, 0.31297
fenwick_PM2R             : 0.28178, 0.28216, 0.29069
count_inversion_mkso     : 0.30286, 0.30290, 0.30652
inv_cnt_ZheHu            : 0.32024, 0.32041, 0.32447
reversePairs_nomanpouigt : 0.45812, 0.45822, 0.46172
Value: 98

Size = 40, hi = 20, 512 loops
solutionE_TimBabych      : 0.05784, 0.05787, 0.05958
solution_TimBabych       : 0.06452, 0.06475, 0.06479
perm_sum_PM2R            : 0.07254, 0.07261, 0.07263
rank_sum_PM2R            : 0.08537, 0.08540, 0.08572
ltree_count_PM2R         : 0.11744, 0.11749, 0.11792
solution_python          : 0.14262, 0.14285, 0.14465
perm_radixI_PM2R         : 0.18774, 0.18776, 0.18922
perm_radixR_PM2R         : 0.19425, 0.19435, 0.19609
bruteforce_loops_PM2R    : 0.21500, 0.21511, 0.21686
perm_fenwick_PM2R        : 0.23338, 0.23375, 0.23674
fenwick_inline_PM2R      : 0.24947, 0.24958, 0.25189
bruteforce_sum_PM2R      : 0.27627, 0.27646, 0.28041
merge_count_BM           : 0.28059, 0.28128, 0.28294
count_inversions_NiklasB : 0.28557, 0.28759, 0.29022
merge_PM2R               : 0.29886, 0.29928, 0.30317
fenwick_PM2R             : 0.30241, 0.30259, 0.35237
count_inversion_mkso     : 0.34252, 0.34356, 0.34441
inv_cnt_ZheHu            : 0.37468, 0.37569, 0.37847
reversePairs_nomanpouigt : 0.50725, 0.50770, 0.50943
Value: 369

Size = 80, hi = 40, 256 loops
solutionE_TimBabych      : 0.06339, 0.06373, 0.06513
solution_TimBabych       : 0.06984, 0.06994, 0.07009
perm_sum_PM2R            : 0.09171, 0.09172, 0.09186
rank_sum_PM2R            : 0.10468, 0.10474, 0.10500
ltree_count_PM2R         : 0.14416, 0.15187, 0.18541
solution_python          : 0.17415, 0.17423, 0.17451
perm_radixI_PM2R         : 0.20676, 0.20681, 0.20936
perm_radixR_PM2R         : 0.21671, 0.21695, 0.21736
perm_fenwick_PM2R        : 0.26197, 0.26252, 0.26264
fenwick_inline_PM2R      : 0.28111, 0.28249, 0.28382
count_inversions_NiklasB : 0.31746, 0.32448, 0.32451
merge_count_BM           : 0.31964, 0.33842, 0.35276
merge_PM2R               : 0.32890, 0.32941, 0.33322
fenwick_PM2R             : 0.34355, 0.34377, 0.34873
count_inversion_mkso     : 0.37689, 0.37698, 0.38079
inv_cnt_ZheHu            : 0.42923, 0.42941, 0.43249
bruteforce_loops_PM2R    : 0.43544, 0.43601, 0.43902
bruteforce_sum_PM2R      : 0.52106, 0.52160, 0.52531
reversePairs_nomanpouigt : 0.57805, 0.58156, 0.58252
Value: 1467

Size = 160, hi = 80, 128 loops
solutionE_TimBabych      : 0.06766, 0.06784, 0.06963
solution_TimBabych       : 0.07433, 0.07489, 0.07516
perm_sum_PM2R            : 0.13143, 0.13175, 0.13179
rank_sum_PM2R            : 0.14428, 0.14440, 0.14922
solution_python          : 0.20072, 0.20076, 0.20084
ltree_count_PM2R         : 0.20314, 0.20583, 0.24776
perm_radixI_PM2R         : 0.23061, 0.23078, 0.23525
perm_radixR_PM2R         : 0.23894, 0.23915, 0.24234
perm_fenwick_PM2R        : 0.30984, 0.31181, 0.31503
fenwick_inline_PM2R      : 0.31933, 0.32680, 0.32722
merge_count_BM           : 0.36003, 0.36387, 0.36409
count_inversions_NiklasB : 0.36796, 0.36814, 0.37106
merge_PM2R               : 0.36847, 0.36848, 0.37127
fenwick_PM2R             : 0.37833, 0.37847, 0.38095
count_inversion_mkso     : 0.42746, 0.42747, 0.43184
inv_cnt_ZheHu            : 0.48969, 0.48974, 0.49293
reversePairs_nomanpouigt : 0.67791, 0.68157, 0.72420
bruteforce_loops_PM2R    : 0.82816, 0.83175, 0.83282
bruteforce_sum_PM2R      : 1.03322, 1.03378, 1.03562
Value: 6194

Size = 320, hi = 160, 64 loops
solutionE_TimBabych      : 0.07467, 0.07470, 0.07483
solution_TimBabych       : 0.08036, 0.08066, 0.08077
perm_sum_PM2R            : 0.21142, 0.21201, 0.25766
solution_python          : 0.22410, 0.22644, 0.22897
rank_sum_PM2R            : 0.22820, 0.22851, 0.22877
ltree_count_PM2R         : 0.24424, 0.24595, 0.24645
perm_radixI_PM2R         : 0.25690, 0.25710, 0.26191
perm_radixR_PM2R         : 0.26501, 0.26504, 0.26729
perm_fenwick_PM2R        : 0.33483, 0.33507, 0.33845
fenwick_inline_PM2R      : 0.34413, 0.34484, 0.35153
merge_count_BM           : 0.39875, 0.39919, 0.40302
fenwick_PM2R             : 0.40434, 0.40439, 0.40845
merge_PM2R               : 0.40814, 0.41531, 0.51417
count_inversions_NiklasB : 0.41681, 0.42009, 0.42128
count_inversion_mkso     : 0.47132, 0.47192, 0.47385
inv_cnt_ZheHu            : 0.54468, 0.54750, 0.54893
reversePairs_nomanpouigt : 0.76164, 0.76389, 0.80357
bruteforce_loops_PM2R    : 1.59125, 1.60430, 1.64131
bruteforce_sum_PM2R      : 2.03734, 2.03834, 2.03975
Value: 24959

Run 2

Size = 640, hi = 320, 8 loops
solutionE_TimBabych      : 0.04135, 0.04374, 0.04575
ltree_count_PM2R         : 0.06738, 0.06758, 0.06874
perm_radixI_PM2R         : 0.06928, 0.06943, 0.07019
fenwick_inline_PM2R      : 0.07850, 0.07856, 0.08059
perm_fenwick_PM2R        : 0.08151, 0.08162, 0.08170
perm_sum_PM2R            : 0.09122, 0.09133, 0.09221
rank_sum_PM2R            : 0.09549, 0.09603, 0.11270
merge_count_BM           : 0.10733, 0.10807, 0.11032
count_inversions_NiklasB : 0.12460, 0.19865, 0.20205
solution_python          : 0.13514, 0.13585, 0.13814

Size = 1280, hi = 640, 8 loops
solutionE_TimBabych      : 0.04714, 0.04742, 0.04752
perm_radixI_PM2R         : 0.15325, 0.15388, 0.15525
solution_python          : 0.15709, 0.15715, 0.16076
fenwick_inline_PM2R      : 0.16048, 0.16160, 0.16403
ltree_count_PM2R         : 0.16213, 0.16238, 0.16428
perm_fenwick_PM2R        : 0.16408, 0.16416, 0.16449
count_inversions_NiklasB : 0.19755, 0.19833, 0.19897
merge_count_BM           : 0.23736, 0.23793, 0.23912
perm_sum_PM2R            : 0.32946, 0.32969, 0.33277
rank_sum_PM2R            : 0.34637, 0.34756, 0.34858

Size = 2560, hi = 1280, 8 loops
solutionE_TimBabych      : 0.10898, 0.11005, 0.11025
perm_radixI_PM2R         : 0.33345, 0.33352, 0.37656
ltree_count_PM2R         : 0.34670, 0.34786, 0.34833
perm_fenwick_PM2R        : 0.34816, 0.34879, 0.35214
fenwick_inline_PM2R      : 0.36196, 0.36455, 0.36741
solution_python          : 0.36498, 0.36637, 0.40887
count_inversions_NiklasB : 0.42274, 0.42745, 0.42995
merge_count_BM           : 0.50799, 0.50898, 0.50917
perm_sum_PM2R            : 1.27773, 1.27897, 1.27951
rank_sum_PM2R            : 1.29728, 1.30389, 1.30448

Size = 5120, hi = 2560, 8 loops
solutionE_TimBabych      : 0.26914, 0.26993, 0.27253
perm_radixI_PM2R         : 0.71416, 0.71634, 0.71753
perm_fenwick_PM2R        : 0.71976, 0.72078, 0.72078
fenwick_inline_PM2R      : 0.72776, 0.72804, 0.73143
ltree_count_PM2R         : 0.81972, 0.82043, 0.82290
solution_python          : 0.83714, 0.83756, 0.83962
count_inversions_NiklasB : 0.87282, 0.87395, 0.92087
merge_count_BM           : 1.09496, 1.09584, 1.10207
rank_sum_PM2R            : 5.02564, 5.06277, 5.06666
perm_sum_PM2R            : 5.09088, 5.12999, 5.13512

Size = 10240, hi = 5120, 8 loops
solutionE_TimBabych      : 0.71556, 0.71718, 0.72201
perm_radixI_PM2R         : 1.54785, 1.55096, 1.55515
perm_fenwick_PM2R        : 1.55103, 1.55353, 1.59298
fenwick_inline_PM2R      : 1.57118, 1.57240, 1.57271
ltree_count_PM2R         : 1.76240, 1.76247, 1.80944
count_inversions_NiklasB : 1.86543, 1.86851, 1.87208
solution_python          : 2.01490, 2.01519, 2.06423
merge_count_BM           : 2.35215, 2.35301, 2.40023
rank_sum_PM2R            : 20.07048, 20.08399, 20.13200
perm_sum_PM2R            : 20.10187, 20.12551, 20.12683

Run 3
Size = 20480, hi = 10240, 4 loops
solutionE_TimBabych      : 1.07636, 1.08243, 1.09569
perm_radixI_PM2R         : 1.59579, 1.60519, 1.61785
perm_fenwick_PM2R        : 1.66885, 1.68549, 1.71109
fenwick_inline_PM2R      : 1.72073, 1.72752, 1.77217
ltree_count_PM2R         : 1.96900, 1.97820, 2.02578
count_inversions_NiklasB : 2.03257, 2.05005, 2.18548
merge_count_BM           : 2.46768, 2.47377, 2.52133
solution_python          : 2.49833, 2.50179, 3.79819

Size = 40960, hi = 20480, 4 loops
solutionE_TimBabych      : 3.51733, 3.52008, 3.56996
perm_radixI_PM2R         : 3.51736, 3.52365, 3.56459
perm_fenwick_PM2R        : 3.76097, 3.80900, 3.87974
fenwick_inline_PM2R      : 3.95099, 3.96300, 3.99748
ltree_count_PM2R         : 4.49866, 4.54652, 5.39716
count_inversions_NiklasB : 4.61851, 4.64303, 4.73026
merge_count_BM           : 5.31945, 5.35378, 5.35951
solution_python          : 6.78756, 6.82911, 6.98217

Size = 81920, hi = 40960, 4 loops
perm_radixI_PM2R         : 7.68723, 7.71986, 7.72135
perm_fenwick_PM2R        : 8.52404, 8.53349, 8.53710
fenwick_inline_PM2R      : 8.97082, 8.97561, 8.98347
ltree_count_PM2R         : 10.01142, 10.01426, 10.03216
count_inversions_NiklasB : 10.60807, 10.62424, 10.70425
merge_count_BM           : 11.42149, 11.42342, 11.47003
solutionE_TimBabych      : 12.83390, 12.83485, 12.89747
solution_python          : 19.66092, 19.67067, 20.72204

Size = 163840, hi = 81920, 4 loops
perm_radixI_PM2R         : 17.14153, 17.16885, 17.22240
perm_fenwick_PM2R        : 19.25944, 19.27844, 20.27568
fenwick_inline_PM2R      : 19.78221, 19.80219, 19.80766
ltree_count_PM2R         : 22.42240, 22.43259, 22.48837
count_inversions_NiklasB : 22.97341, 23.01516, 23.98052
merge_count_BM           : 24.42683, 24.48559, 24.51488
solutionE_TimBabych      : 60.96006, 61.20145, 63.71835
solution_python          : 73.75132, 73.79854, 73.95874

Size = 327680, hi = 163840, 4 loops
perm_radixI_PM2R         : 36.56715, 36.60221, 37.05071
perm_fenwick_PM2R        : 42.21616, 42.21838, 42.26053
fenwick_inline_PM2R      : 43.04987, 43.09075, 43.13287
ltree_count_PM2R         : 49.87400, 50.08509, 50.69292
count_inversions_NiklasB : 50.74591, 50.75012, 50.75551
merge_count_BM           : 52.37284, 52.51491, 53.43003
solutionE_TimBabych      : 373.67198, 377.03341, 377.42360
solution_python          : 411.69178, 411.92691, 412.83856

Size = 655360, hi = 327680, 4 loops
perm_radixI_PM2R         : 78.51927, 78.66327, 79.46325
perm_fenwick_PM2R        : 90.64711, 90.80328, 91.76126
fenwick_inline_PM2R      : 93.32482, 93.39086, 94.28880
count_inversions_NiklasB : 107.74393, 107.80036, 108.71443
ltree_count_PM2R         : 109.11328, 109.23592, 110.18247
merge_count_BM           : 111.05633, 111.07840, 112.05861
solutionE_TimBabych      : 1830.46443, 1836.39960, 1849.53918
solution_python          : 1911.03692, 1912.04484, 1914.69786
PM 2Bague
la source
1

Voici un code C pour les inversions de comptage

#include <stdio.h>
#include <stdlib.h>

int  _mergeSort(int arr[], int temp[], int left, int right);
int merge(int arr[], int temp[], int left, int mid, int right);

/* This function sorts the input array and returns the
   number of inversions in the array */
int mergeSort(int arr[], int array_size)
{
    int *temp = (int *)malloc(sizeof(int)*array_size);
    return _mergeSort(arr, temp, 0, array_size - 1);
}

/* An auxiliary recursive function that sorts the input array and
  returns the number of inversions in the array. */
int _mergeSort(int arr[], int temp[], int left, int right)
{
  int mid, inv_count = 0;
  if (right > left)
  {
    /* Divide the array into two parts and call _mergeSortAndCountInv()
       for each of the parts */
    mid = (right + left)/2;

    /* Inversion count will be sum of inversions in left-part, right-part
      and number of inversions in merging */
    inv_count  = _mergeSort(arr, temp, left, mid);
    inv_count += _mergeSort(arr, temp, mid+1, right);

    /*Merge the two parts*/
    inv_count += merge(arr, temp, left, mid+1, right);
  }
  return inv_count;
}

/* This funt merges two sorted arrays and returns inversion count in
   the arrays.*/
int merge(int arr[], int temp[], int left, int mid, int right)
{
  int i, j, k;
  int inv_count = 0;

  i = left; /* i is index for left subarray*/
  j = mid;  /* i is index for right subarray*/
  k = left; /* i is index for resultant merged subarray*/
  while ((i <= mid - 1) && (j <= right))
  {
    if (arr[i] <= arr[j])
    {
      temp[k++] = arr[i++];
    }
    else
    {
      temp[k++] = arr[j++];

     /*this is tricky -- see above explanation/diagram for merge()*/
      inv_count = inv_count + (mid - i);
    }
  }

  /* Copy the remaining elements of left subarray
   (if there are any) to temp*/
  while (i <= mid - 1)
    temp[k++] = arr[i++];

  /* Copy the remaining elements of right subarray
   (if there are any) to temp*/
  while (j <= right)
    temp[k++] = arr[j++];

  /*Copy back the merged elements to original array*/
  for (i=left; i <= right; i++)
    arr[i] = temp[i];

  return inv_count;
}

/* Driver progra to test above functions */
int main(int argv, char** args)
{
  int arr[] = {1, 20, 6, 4, 5};
  printf(" Number of inversions are %d \n", mergeSort(arr, 5));
  getchar();
  return 0;
}

Une explication a été donnée en détail ici: http://www.geeksforgeeks.org/counting-inversions/

banarun
la source
1

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

  • 0 5 10 15

fusionné avec

  • 1 6 22

nous avons 3 + 2 + 0 = 5 inversions:

  • 1 avec {5, 10, 15}
  • 6 avec {10, 15}
  • 22 avec {}

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.

    public class FindInversions {

    public static int solution(int[] input) {
        if (input == null)
            return 0;
        int[] helper = new int[input.length];
        return mergeSort(0, input.length - 1, input, helper);
    }

    public static int mergeSort(int low, int high, int[] input, int[] helper) {
        int inversionCount = 0;
        if (low < high) {
            int medium = low + (high - low) / 2;
            inversionCount += mergeSort(low, medium, input, helper);
            inversionCount += mergeSort(medium + 1, high, input, helper);
            inversionCount += merge(low, medium, high, input, helper);
        }
        return inversionCount;
    }

    public static int merge(int low, int medium, int high, int[] input, int[] helper) {
        int inversionCount = 0;

        for (int i = low; i <= high; i++)
            helper[i] = input[i];

        int i = low;
        int j = medium + 1;
        int k = low;

        while (i <= medium && j <= high) {
            if (helper[i] <= helper[j]) {
                input[k] = helper[i];
                i++;
            } else {
                input[k] = helper[j];
                // the number of elements in the first half which the j element needs to jump over.
                // there is an inversion between each of those elements and j.
                inversionCount += (medium + 1 - i);
                j++;
            }
            k++;
        }

        // finish writing back in the input the elements from the first part
        while (i <= medium) {
            input[k] = helper[i];
            i++;
            k++;
        }
        return inversionCount;
    }

}
Andrey Petrov
la source
1

Voici l'implémentation de O (n * log (n)) perl:

sub sort_and_count {
    my ($arr, $n) = @_;
    return ($arr, 0) unless $n > 1;

    my $mid = $n % 2 == 1 ? ($n-1)/2 : $n/2;
    my @left = @$arr[0..$mid-1];
    my @right = @$arr[$mid..$n-1];

    my ($sleft, $x) = sort_and_count( \@left, $mid );
    my ($sright, $y) = sort_and_count( \@right, $n-$mid);
    my ($merged, $z) = merge_and_countsplitinv( $sleft, $sright, $n );

    return ($merged, $x+$y+$z);
}

sub merge_and_countsplitinv {
    my ($left, $right, $n) = @_;

    my ($l_c, $r_c) = ($#$left+1, $#$right+1);
    my ($i, $j) = (0, 0);
    my @merged;
    my $inv = 0;

    for my $k (0..$n-1) {
        if ($i<$l_c && $j<$r_c) {
            if ( $left->[$i] < $right->[$j]) {
                push @merged, $left->[$i];
                $i+=1;
            } else {
                push @merged, $right->[$j];
                $j+=1;
                $inv += $l_c - $i;
            }
        } else {
            if ($i>=$l_c) {
                push @merged, @$right[ $j..$#$right ];
            } else {
                push @merged, @$left[ $i..$#$left ];
            }
            last;
        }
    }

    return (\@merged, $inv);
}
Omid
la source
1

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.

def binarySearch(alist, item):
    first = 0
    last = len(alist) - 1
    found = False

    while first <= last and not found:
        midpoint = (first + last)//2
        if alist[midpoint] == item:
            return midpoint
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1

def solution(A):

    B = list(A)
    B.sort()
    inversion_count = 0
    for i in range(len(A)):
        j = binarySearch(B, A[i])
        while B[j] == B[j - 1]:
            if j < 1:
                break
            j -= 1

        inversion_count += j
        B.pop(j)

    if inversion_count > 1000000000:
        return -1
    else:
        return inversion_count

print solution([4, 10, 11, 1, 3, 9, 10])
python
la source
J'ai publié une réponse qui timeitcompare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage.
PM 2Ring
1

Eh bien, j'ai une solution différente, mais j'ai peur que cela ne fonctionne que pour des éléments de tableau distincts.

//Code
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int i,n;
    cin >> n;
    int arr[n],inv[n];
    for(i=0;i<n;i++){
        cin >> arr[i];
    }
    vector<int> v;
    v.push_back(arr[n-1]);
    inv[n-1]=0;
    for(i=n-2;i>=0;i--){
        auto it = lower_bound(v.begin(),v.end(),arr[i]); 
        //calculating least element in vector v which is greater than arr[i]
        inv[i]=it-v.begin();
        //calculating distance from starting of vector
        v.insert(it,arr[i]);
        //inserting that element into vector v
    }
    for(i=0;i<n;i++){
        cout << inv[i] << " ";
    }
    cout << endl;
    return 0;
}

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é.

//INPUT     
4
2 1 4 3

//OUTPUT    
1 0 1 0

//To calculate total inversion count just add up all the elements in output array
Varun Garg
la source
1

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.

import bisect
def solution(A):
    sorted_left = []
    res = 0
    for i in xrange(1, len(A)):
        bisect.insort_left(sorted_left, A[i-1])
        # i is also the length of sorted_left
        res += (i - bisect.bisect(sorted_left, A[i]))
    return res
Tim Babych
la source
1
J'ai publié une réponse qui timeitcompare toutes les réponses Python à cette question, elle inclut donc votre code. Vous voudrez peut-être consulter les résultats de chronométrage. : D
PM 2Bague
0

La réponse facile O (n ^ 2) est d'utiliser des boucles for imbriquées et d'incrémenter un compteur pour chaque inversion

int counter = 0;

for(int i = 0; i < n - 1; i++)
{
    for(int j = i+1; j < n; j++)
    {
        if( A[i] > A[j] )
        {
            counter++;
        }
    }
}

return counter;

Maintenant, je suppose que vous voulez une solution plus efficace, je vais y réfléchir.

mbillard
la source
3
Pour les questions relatives aux devoirs, il est préférable de donner des suggestions utiles plutôt qu'une solution concrète. Apprenez à un homme à pêcher.
Doctor Jones
3
C'est la solution évidente que tous les autres élèves obtiendront en premier, je suppose que leur enseignant veut une meilleure implémentation qui leur rapportera plus de points.
mbillard
Pas nécessairement, cela dépend du niveau du cours de programmation. Ce n'est pas si simple pour un débutant.
Doctor Jones
ils veulent probablement une solution n * log (n)
aderesh
0

Une solution possible en C ++ satisfaisant l'exigence de complexité en temps O (N * log (N)) serait la suivante.

#include <algorithm>

vector<int> merge(vector<int>left, vector<int>right, int &counter)
{

    vector<int> result;

    vector<int>::iterator it_l=left.begin();
    vector<int>::iterator it_r=right.begin();

    int index_left=0;

    while(it_l!=left.end() || it_r!=right.end())
    {

        // the following is true if we are finished with the left vector 
        // OR if the value in the right vector is the smaller one.

        if(it_l==left.end() || (it_r!=right.end() && *it_r<*it_l) )
        {
            result.push_back(*it_r);
            it_r++;

            // increase inversion counter
            counter+=left.size()-index_left;
        }
        else
        {
            result.push_back(*it_l);
            it_l++;
            index_left++;

        }
    }

    return result;
}

vector<int> merge_sort_and_count(vector<int> A, int &counter)
{

    int N=A.size();
    if(N==1)return A;

    vector<int> left(A.begin(),A.begin()+N/2);
    vector<int> right(A.begin()+N/2,A.end());

    left=merge_sort_and_count(left,counter);
    right=merge_sort_and_count(right,counter);


    return merge(left, right, counter);

}

Il diffère d'un tri par fusion régulier uniquement par le compteur.

oo_miguel
la source
Cela semble à peu près la même chose que les solutions Java et Python publiées plus tôt, apparemment en utilisant le même algorithme, et je ne pense donc pas que cela ajoute beaucoup de valeur au-delà d'elles.
Bernhard Barker
0

Voici ma solution O (n log n) dans Ruby:

def solution(t)
    sorted, inversion_count = sort_inversion_count(t)
    return inversion_count
end

def sort_inversion_count(t)
    midpoint = t.length / 2
    left_half = t[0...midpoint]
    right_half = t[midpoint..t.length]

    if midpoint == 0
        return t, 0
    end

    sorted_left_half, left_half_inversion_count = sort_inversion_count(left_half)
    sorted_right_half, right_half_inversion_count = sort_inversion_count(right_half)

    sorted = []
    inversion_count = 0
    while sorted_left_half.length > 0 or sorted_right_half.length > 0
        if sorted_left_half.empty?
            sorted.push sorted_right_half.shift
        elsif sorted_right_half.empty?
            sorted.push sorted_left_half.shift
        else
            if sorted_left_half[0] > sorted_right_half[0]
                inversion_count += sorted_left_half.length
                sorted.push sorted_right_half.shift
            else
                sorted.push sorted_left_half.shift
            end
        end
    end

    return sorted, inversion_count + left_half_inversion_count + right_half_inversion_count
end

Et quelques cas de test:

require "minitest/autorun"

class TestCodility < Minitest::Test
    def test_given_example
        a = [-1, 6, 3, 4, 7, 4]
        assert_equal solution(a), 4
    end

    def test_empty
        a = []
        assert_equal solution(a), 0
    end

    def test_singleton
        a = [0]
        assert_equal solution(a), 0
    end

    def test_none
        a = [1,2,3,4,5,6,7]
        assert_equal solution(a), 0
    end

    def test_all
        a = [5,4,3,2,1]
        assert_equal solution(a), 10
    end

    def test_clones
        a = [4,4,4,4,4,4]
        assert_equal solution(a), 0
    end
end
Brandon
la source
0

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 sousmergeToParent méthode où je compte l'inversion sous la condition else de(left[leftunPicked] < right[rightunPicked])

public class TestInversionThruMergeSort {
    
    static int count =0;

    public static void main(String[] args) {
        int[] arr = {6, 9, 1, 14, 8, 12, 3, 2};
        

        partition(arr);

        for (int i = 0; i < arr.length; i++) {

            System.out.println(arr[i]);
        }
        
        System.out.println("inversions are "+count);

    }

    public static void partition(int[] arr) {

        if (arr.length > 1) {

            int mid = (arr.length) / 2;
            int[] left = null;

            if (mid > 0) {
                left = new int[mid];

                for (int i = 0; i < mid; i++) {
                    left[i] = arr[i];
                }
            }

            int[] right = new int[arr.length - left.length];

            if ((arr.length - left.length) > 0) {
                int j = 0;
                for (int i = mid; i < arr.length; i++) {
                    right[j] = arr[i];
                    ++j;
                }
            }

            partition(left);
            partition(right);
            mergeToParent(left, right, arr);
        }

    }

    public static void mergeToParent(int[] left, int[] right, int[] parent) {

        int leftunPicked = 0;
        int rightunPicked = 0;
        int parentIndex = -1;

        while (rightunPicked < right.length && leftunPicked < left.length) {

            if (left[leftunPicked] < right[rightunPicked]) {
                parent[++parentIndex] = left[leftunPicked];
                ++leftunPicked;

            } else {
                count = count + left.length-leftunPicked;
                if ((rightunPicked < right.length)) {
                    parent[++parentIndex] = right[rightunPicked];
                    ++rightunPicked;
                }
            }

        }

        while (leftunPicked < left.length) {
            parent[++parentIndex] = left[leftunPicked];
            ++leftunPicked;
        }

        while (rightunPicked < right.length) {
            parent[++parentIndex] = right[rightunPicked];
            ++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).

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;


public class TestInversion {

    public static void main(String[] args) {
        
        Integer [] arr1 = {6, 9, 1, 14, 8, 12, 3, 2};
        
        List<Integer> arr = new ArrayList(Arrays.asList(arr1));
        List<Integer> sortArr = new ArrayList<Integer>();
        
        for(int i=0;i<arr.size();i++){
            sortArr.add(arr.get(i));
         
        }
        
            
        Collections.sort(sortArr);
        
        int inversion = 0;
        
        Iterator<Integer> iter = arr.iterator();
        
        while(iter.hasNext()){
            
            Integer el = (Integer)iter.next();
            int index = sortArr.indexOf(el);
            
            if(index+1 > 1){
                inversion = inversion + ((index+1)-1);
            }
            
            //iter.remove();
            sortArr.remove(el);
            
        }
        
        System.out.println("Inversions are "+inversion);
        
        
        

    }


}
M Sach
la source
0

Le nombre maximum d'inversions possibles pour une liste de taille npourrait être généralisé par une expression:

maxPossibleInversions = (n * (n-1) ) / 2

Donc, pour un tableau de taille 6maximale, 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:

  • Divisez le tableau en deux
  • Appelez la routine mergeSort. Si l'élément du sous-tableau de gauche est supérieur à celui du sous-tableau de droite, faitesinversionCount += leftSubArray.length

C'est tout!

Ceci est un exemple simple, j'ai fait en utilisant Javascript:

var arr = [6,5,4,3,2,1]; // Sample input array

var inversionCount = 0;

function mergeSort(arr) {
    if(arr.length == 1)
        return arr;

    if(arr.length > 1) {
        let breakpoint = Math.ceil((arr.length/2));
        // Left list starts with 0, breakpoint-1
        let leftList = arr.slice(0,breakpoint);
        // Right list starts with breakpoint, length-1
        let rightList = arr.slice(breakpoint,arr.length);

        // Make a recursive call
        leftList = mergeSort(leftList);
        rightList = mergeSort(rightList);

        var a = merge(leftList,rightList);
        return a;
    }
}

function merge(leftList,rightList) {
    let result = [];
    while(leftList.length && rightList.length) {
        /**
         * The shift() method removes the first element from an array
         * and returns that element. This method changes the length
         * of the array.
         */
        if(leftList[0] <= rightList[0]) {
            result.push(leftList.shift());
        }else{
            inversionCount += leftList.length;
            result.push(rightList.shift());
        }
    }

    while(leftList.length)
        result.push(leftList.shift());

    while(rightList.length)
        result.push(rightList.shift());

    console.log(result);
    return result;
}

mergeSort(arr);
console.log('Number of inversions: ' + inversionCount);
Suhail Gupta
la source
0

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

nSwaps += mid + 1 - iL 

(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é.

func merge(arr: inout [Int], arr2: inout [Int], low: Int, mid: Int, high: Int) -> Int {
    var nSwaps = 0;

    var i = low;
    var iL = low;
    var iR = mid + 1;

    while iL <= mid && iR <= high {
        if arr2[iL] <= arr2[iR] {
            arr[i] = arr2[iL]
            iL += 1
            i += 1
        } else {
            arr[i] = arr2[iR]
            nSwaps += mid + 1 - iL
            iR += 1
            i += 1
        }
    }

    while iL <= mid {
        arr[i] = arr2[iL]
        iL += 1
        i += 1
    }

    while iR <= high {
        arr[i] = arr2[iR]
        iR += 1
        i += 1
    }

    return nSwaps
}

func mergeSort(arr: inout [Int]) -> Int {
    var arr2 = arr
    let nSwaps = mergeSort(arr: &arr, arr2: &arr2, low: 0, high: arr.count-1)
    return nSwaps
}

func mergeSort(arr: inout [Int], arr2: inout [Int], low: Int, high: Int) -> Int {

    if low >= high {
        return 0
    }

    let mid = low + ((high - low) / 2)

    var nSwaps = 0;
    nSwaps += mergeSort(arr: &arr2, arr2: &arr, low: low, high: mid)
    nSwaps += mergeSort(arr: &arr2, arr2: &arr, low: mid+1, high: high)
    nSwaps += merge(arr: &arr, arr2: &arr2, low: low, mid: mid, high: high)

    return nSwaps
}

var arrayToSort: [Int] = [2, 1, 3, 1, 2]
let nSwaps = mergeSort(arr: &arrayToSort)

print(arrayToSort) // [1, 1, 2, 2, 3]
print(nSwaps) // 4
Davejlin
la source
0

La plupart des réponses sont basées sur, MergeSortmais ce n'est pas la seule façon de résoudre ce problème.O(nlogn)

Je vais discuter de quelques approches.

  1. Utiliser un Balanced Binary Search Tree

    • Augmentez votre arbre pour stocker les fréquences des éléments en double.
    • L'idée est de continuer à compter de plus grands nœuds lorsque l'arbre est parcouru de la racine à une feuille pour l'insertion.

Quelque chose comme ça.

Node *insert(Node* root, int data, int& count){
    if(!root) return new Node(data);
    if(root->data == data){
        root->freq++;
        count += getSize(root->right);
    }
    else if(root->data > data){
        count += getSize(root->right) + root->freq;
        root->left = insert(root->left, data, count);
    }
    else root->right = insert(root->right, data, count);
    return balance(root);
}

int getCount(int *a, int n){
    int c = 0;
    Node *root = NULL;
    for(auto i=0; i<n; i++) root = insert(root, a[i], c);
    return c;
}
  1. Utiliser un Binary Indexed Tree
    • Créez un BIT de sommation.
    • Bouclez à partir de la fin et commencez à trouver le nombre d'éléments plus importants.
int getInversions(int[] a) {
    int n = a.length, inversions = 0;
    int[] bit = new int[n+1];
    compress(a);
    BIT b = new BIT();
    for (int i=n-1; i>=0; i--) {
         inversions += b.getSum(bit, a[i] - 1);
         b.update(bit, n, a[i], 1);
     }
     return inversions;
}
  1. Utiliser un Segment Tree
    • Créez une arborescence de segments de sommation.
    • Boucle à partir de la fin du tableau et interrogation entre [0, a[i]-1]et mise à joura[i] with 1
int getInversions(int *a, int n) {
    int N = n + 1, c = 0;
    compress(a, n);
    int tree[N<<1] = {0};
    for (int i=n-1; i>=0; i--) {
        c+= query(tree, N, 0, a[i] - 1);
        update(tree, N, a[i], 1);
    }
    return c;
}

Aussi, lors de l'utilisation BITou Segment-Treeune bonne idée est de faireCoordinate compression

void compress(int *a, int n) {
    int temp[n];
    for (int i=0; i<n; i++) temp[i] = a[i];
    sort(temp, temp+n);
    for (int i=0; i<n; i++) a[i] = lower_bound(temp, temp+n, a[i]) - temp + 1;
}
Ankit Sharma
la source
0

C ++ Θ (n lg n) Solution à l'impression de paires qui constituent en comptage d'inversion.

int merge(vector<int>&nums , int low , int mid , int high){
    int size1 = mid - low +1;
    int size2= high - mid;
    vector<int>left;
    vector<int>right;
    for(int i = 0  ; i < size1 ; ++i){
        left.push_back(nums[low+i]);
    }
    for(int i = 0 ; i <size2 ; ++i){
        right.push_back(nums[mid+i+1]);
    }
    left.push_back(INT_MAX);
    right.push_back(INT_MAX);
    int i = 0 ;
    int j = 0;
    int start  = low;
    int inversion = 0 ;
    while(i < size1 && j < size2){
        if(left[i]<right[j]){
            nums[start] = left[i];
            start++;
            i++;
        }else{
            for(int l = i ; l < size1; ++l){
                cout<<"("<<left[l]<<","<<right[j]<<")"<<endl;
            }
            inversion += size1 - i;
            nums[start] = right[j];
            start++;
            j++;
        }
    }
    if(i == size1){
        for(int c = j ; c< size2 ; ++c){
            nums[start] = right[c];
            start++;
        }
    }
    if(j == size2){
        for(int c =  i ; c< size1 ; ++c){
            nums[start] = left[c];
            start++;
        }
    }
    return inversion;
}
int inversion_count(vector<int>& nums , int low , int high){
    if(high>low){
        int mid = low + (high-low)/2;
        int left = inversion_count(nums,low,mid);
        int right = inversion_count(nums,mid+1,high);
        int inversion = merge(nums,low,mid,high) + left + right;
        return inversion;
    }
    return 0 ;
}
Mec problématique
la source
-1

Utilisez mergesort, dans l'étape de fusion, incrémentez le compteur si le nombre copié en sortie provient du tableau de droite.

rkatiyar
la source
Incrémenter le compteur (vraisemblablement de un) pour chaque élément vous donnera trop peu d'inversions.
Bernhard Barker le
-1

J'ai récemment dû faire ça en R:

inversionNumber <- function(x){
    mergeSort <- function(x){
        if(length(x) == 1){
            inv <- 0
        } else {
            n <- length(x)
            n1 <- ceiling(n/2)
            n2 <- n-n1
            y1 <- mergeSort(x[1:n1])
            y2 <- mergeSort(x[n1+1:n2])
            inv <- y1$inversions + y2$inversions
            x1 <- y1$sortedVector
            x2 <- y2$sortedVector
            i1 <- 1
            i2 <- 1
            while(i1+i2 <= n1+n2+1){
                if(i2 > n2 || i1 <= n1 && x1[i1] <= x2[i2]){
                    x[i1+i2-1] <- x1[i1]
                    i1 <- i1 + 1
                } else {
                    inv <- inv + n1 + 1 - i1
                    x[i1+i2-1] <- x2[i2]
                    i2 <- i2 + 1
                }
            }
        }
        return (list(inversions=inv,sortedVector=x))
    }
    r <- mergeSort(x)
    return (r$inversions)
}
Musclé
la source
1
@Dukeling Qu'est-ce qui vous a incité à retirer votre commentaire mais pas votre vote négatif?
Museful