web-dev-qa-db-fra.com

Pourquoi max est-il plus lent que le tri?

J'ai trouvé que max est plus lent que la fonction sort dans Python 2 et 3.

Python 2

$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 239 usec per loop
$ python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'max(a)'        
1000 loops, best of 3: 342 usec per loop

Python 3

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a.sort();a[-1]'
1000 loops, best of 3: 252 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a)'
1000 loops, best of 3: 371 usec per loop

Pourquoi estmax (O(n)) plus lent que la fonction sort (O(nlogn))?

91
WeizhongTu

Vous devez être très prudent lorsque vous utilisez le module timeit en Python.

python -m timeit -s 'import random;a=range(10000);random.shuffle(a)' 'a.sort();a[-1]'

Ici, le code d'initialisation s'exécute une fois pour produire un tableau randomisé a. Ensuite, le reste du code est exécuté plusieurs fois. La première fois, il trie le tableau, mais une fois sur deux, vous appelez la méthode de tri sur un tableau déjà trié. Seul le temps le plus rapide est retourné, donc vous chronométrez réellement le temps qu'il faut Python pour trier un tableau déjà trié.

Une partie de l'algorithme de tri de Python consiste à détecter lorsque le tableau est déjà partiellement ou complètement trié. Une fois complètement trié, il suffit de parcourir une fois le tableau pour le détecter, puis il s'arrête.

Si vous avez plutôt essayé:

python -m timeit -s 'import random;a=range(100000);random.shuffle(a)' 'sorted(a)[-1]'

alors le tri se produit sur chaque boucle de synchronisation et vous pouvez voir que le temps pour trier un tableau est en effet beaucoup plus long que pour trouver simplement la valeur maximale.

Edit: @ skyking's answer explique la partie que je n'ai pas expliquée: a.sort() sait qu'elle travaille sur un liste afin de pouvoir accéder directement aux éléments. max(a) fonctionne sur tout itérable arbitraire donc doit utiliser une itération générique.

124
Duncan

Tout d'abord, notez que max() utilise le protocole itérateur , tandis que list.sort() utilise un code ad hoc . De toute évidence, l'utilisation d'un itérateur est une surcharge importante, c'est pourquoi vous observez cette différence de synchronisation.

Cependant, à part cela, vos tests ne sont pas équitables. Vous exécutez plus d'une fois a.sort() sur la même liste. algorithme utilisé par Python est spécialement conçu pour être rapide pour les données déjà (partiellement) triées. Vos tests indiquent que l'algorithme fait bien son travail.

Ce sont des tests équitables:

$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'max(a[:])'
1000 loops, best of 3: 227 usec per loop
$ python3 -m timeit -s 'import random;a=list(range(10000));random.shuffle(a)' 'a[:].sort()'
100 loops, best of 3: 2.28 msec per loop

Ici, je crée une copie de la liste à chaque fois. Comme vous pouvez le voir, l'ordre de grandeur des résultats est différent: micro- vs millisecondes, comme on pourrait s'y attendre.

Et rappelez-vous: big-Oh spécifie une limite supérieure! La limite inférieure de l'algorithme de tri de Python est Ω ( n ). Être O ( n log n ) n'implique pas automatiquement que chaque exécution prend un temps proportionnel à n log n . Cela n'implique même pas qu'il doit être plus lent qu'un algorithme O ( n ), mais c'est une autre histoire. Ce qui est important à comprendre, c'est que dans certains cas favorables, un O ( n log n ) l'algorithme peut fonctionner en O ( n ) fois ou moins.

88
Andrea Corbellini

Cela peut être dû au fait que l.sort Est membre de list tandis que max est une fonction générique. Cela signifie que l.sort Peut s'appuyer sur la représentation interne de list tandis que max devra passer par le protocole générique de l'itérateur.

Cela rend chaque élément récupéré pour l.sort Plus rapide que chaque élément récupéré par max.

Je suppose que si vous utilisez plutôt sorted(a) vous obtiendrez le résultat plus lentement que max(a).

31
skyking