web-dev-qa-db-fra.com

Existe-t-il un moyen de mesurer le tri d'une liste?

Existe-t-il un moyen de mesurer le tri d'une liste?

Je veux dire, il ne s'agit pas de savoir si une liste est triée ou non (booléenne), mais quelque chose comme un rapport de "tri", quelque chose comme le coefficient de corrélation dans les statistiques.

Par exemple,

  • Si les éléments d'une liste sont en ordre croissant, alors son taux serait de 1,0

  • Si la liste est triée décroissante, son taux serait de -1,0

  • Si la liste est presque triée par ordre croissant, son taux serait de 0,9 ou une valeur proche de 1.

  • Si la liste n'est pas triée du tout (aléatoire), son taux serait proche de 0

J'écris une petite bibliothèque en Scala pour la pratique. Je pense qu'un taux de tri serait utile, mais je ne trouve aucune information sur quelque chose comme ça. Peut-être que je ne sais pas adéquat termes pour le concept.

160
Josell

Vous pouvez simplement compter le nombre d'inversions dans la liste.

Inversion

Une inversion dans une séquence d'éléments de type T est une paire d'éléments de séquence qui apparaissent dans le désordre selon un ordre < Sur l'ensemble des T.

De Wikipedia :

Formellement, soit A(1), A(2), ..., A(n) une suite de n nombres.
Si i < j Et A(i) > A(j), alors la paire (i,j) Est appelée une inversion de A.

Le nombre d'inversion d'une séquence est une mesure courante de son tri.
Formellement, le nombre d'inversion est défini comme le nombre d'inversions, c'est-à-dire,

definition

Pour rendre ces définitions plus claires, considérez l'exemple de séquence 9, 5, 7, 6. Cette séquence a les inversions (0,1), (0,2), (0,3), (2,3) Et le numéro d'inversion 4.

Si vous voulez une valeur entre 0 Et 1, Vous pouvez diviser le nombre d'inversion par N choose 2.

Pour créer réellement un algorithme pour calculer ce score pour le tri d'une liste, vous avez deux approches:

Approche 1 (déterministe)

Modifiez votre algorithme de tri préféré pour garder une trace du nombre d'inversions qu'il corrige en cours d'exécution. Bien que cela ne soit pas trivial et ait différentes implémentations selon l'algorithme de tri que vous choisissez, vous vous retrouverez avec un algorithme qui n'est pas plus cher (en termes de complexité) que l'algorithme de tri avec lequel vous avez commencé.

Si vous suivez cette voie, sachez que ce n'est pas aussi simple que de compter les "swaps". Mergesort, par exemple, est le pire des cas O(N log N), mais s'il est exécuté sur une liste triée par ordre décroissant, il corrigera toutes les inversions N choose 2. C'est O(N^2) inversions corrigées dans O(N log N) opérations. Ainsi, certaines opérations doivent inévitablement corriger plus d'une inversion à la fois. Vous devez être prudent avec votre mise en œuvre. Remarque: vous pouvez le faire avec la complexité de O(N log N), c'est juste délicat.

Connexes: calcul du nombre d '"inversions" dans une permutation

Approche 2 (stochastique)

  • Échantillonner des paires au hasard (i,j), Où i != j
  • Pour chaque paire, déterminez si list[min(i,j)] < list[max(i,j)] (0 ou 1)
  • Calculez la moyenne de ces comparaisons, puis normalisez par N choose 2

Personnellement, j'opterais pour l'approche stochastique, sauf si vous avez une exigence d'exactitude - ne serait-ce que parce qu'elle est si facile à mettre en œuvre.


Si ce que vous voulez vraiment est une valeur (z') Entre -1 (Tri décroissant) à 1 (Tri croissant), vous pouvez simplement mapper la valeur ci-dessus (z), qui se situe entre 0 (tri croissant) et 1 (tri décroissant), à cette plage en utilisant cette formule:

z' = -2 * z + 1
142
Timothy Shields

La mesure traditionnelle du tri d'une liste (ou d'une autre structure séquentielle) est le nombre d'inversions.

Le nombre d'inversions est le nombre de paires (a, b) st index de a <b AND b << une. À ces fins << représente la relation de tri que vous choisissez pour votre tri particulier.

Une liste entièrement triée n'a pas d'inversions et une liste complètement inversée a le nombre maximal d'inversions.

24
Marcin

Vous pouvez utiliser une corrélation réelle.

Supposons que pour chaque élément de la liste triée, vous affectez un rang entier à partir de zéro. Notez qu'un graphique de l'indice de position des éléments en fonction du rang ressemblera à des points en ligne droite (corrélation de 1,0 entre la position et le rang).

Vous pouvez calculer une corrélation sur ces données. Pour un tri inversé, vous obtiendrez -1 et ainsi de suite.

17
Kaz

Il y a eu d'excellentes réponses, et je voudrais ajouter un aspect mathématique pour être complet:

  • Vous pouvez mesurer le tri d'une liste en mesurant sa corrélation avec une liste triée. Pour ce faire, vous pouvez utiliser la corrélation de rang (la plus connue étant Spearman's ), qui est exactement la même que la corrélation habituelle, mais elle utilise le rang des éléments dans une liste au lieu des valeurs analogiques de ses articles.

  • De nombreuses extensions existent, comme un coefficient de corrélation (+1 pour le tri exact, -1 pour l'inversion exacte)

  • Cela vous permet d'avoir des propriétés statistiques pour cette mesure, comme le théorème de la limite centrale permutationnelle, qui vous permet de connaître la distribution de cette mesure pour les listes aléatoires.

4
meduz

Mis à part le nombre d'inversion, pour les listes numériques, la distance carrée moyenne de l'état trié est imaginable:

#! Ruby
d = -> a { a.Zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case
3
Boris Stitnicky

Je ne suis pas sûr de la "meilleure" méthode, mais une méthode simple serait de comparer chaque élément avec celui qui le suit, en incrémentant un compteur si element2> element 1 (ou ce que vous voulez tester), puis de diviser par le nombre total d'éléments. Cela devrait vous donner un pourcentage.

1
user2369405

Je compterais les comparaisons et je les diviserais par le nombre total de comparaisons. Voici un exemple simple Python .

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result
1
ibrahim

Que diriez-vous quelque chose comme ça?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   Elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this Zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in Zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()
0
dstromberg

Si vous prenez votre liste, calculez les rangs des valeurs de cette liste et appelez la liste des rangs Y et une autre liste, X qui contient les entiers de 1 À length(Y), vous pouvez obtenir exactement la mesure de tri que vous recherchez en calculant le coefficient de corrélation , r, entre les deux listes.

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

Pour une liste entièrement triée, r = 1.0, Pour une liste triée inversement, r=-1.0 Et le r varie entre ces limites pour divers degrés de tri.

Un problème possible avec cette approche, selon l'application, est que le calcul du rang de chaque élément de la liste équivaut à le trier, il s'agit donc d'une opération O (n log n).

0
Simon