Je travaille sur l'algorithme Leetcode Problème 977. Carrés d'une matrice triée.
Pourquoi les soumissions utilisant la méthode intégrée triée sont plus rapides que ma méthode Traverselle O (n) comme ci-dessous? P>
L'entrée est une triée (non Diminution de la liste d'ordre) avec des entiers. p>
échantillon 208 MS Soumission: P>
class Solution: def sortedSquares(self, A: List[int]) -> List[int]: start = 0 end = len(A)-1 B = [] while start <= end: if abs(A[start]) >= abs(A[end]): B.append(A[start] * A[start]) start += 1 else: B.append(A[end] * A[end]) end -= 1 B.reverse() return B
5 Réponses :
Juste parce que votre algorithme a un meilleur temps de fonctionnement du pire des cas ne signifie pas que ce sera mieux dans la pratique. La méthode de tri intégrée de Python est hautement optimisée, de sorte que son temps d'exécution peut être De plus, lorsque votre liste d'entrée est triée (une partie non négative), il peut y avoir une certaine optimisation pour des listes presque triées dans ce cas (pas sûr, n'a jamais examiné la mise en œuvre de la sorte de Python). P> cnlg (n) code> pour un
C code> relativement petit, tandis que votre algorithme, tout en étant
o (n) code>, pourrait avoir une constante vraiment élevée
d code> pour
dn code>. Nous ne savons pas quelle est votre entrée, il pourrait donc s'agir d'un tableau de 10000 éléments, pour lequel
d code> est toujours significativement plus grand que
CLG (10000) code>. p>
Vous pouvez tout pré-carré (donc aucun ABS () code> est nécessaire, et surtout pas répété
ABS () code> appelle pour un seul élément):
C = [x*x for x in A]
start = 0
end = len(A)-1
B = []
while start <= end:
if C[start] >= C[end]:
B.append(C[start])
start += 1
else:
B.append(C[end])
end -= 1
B.reverse()
return B
Ceci est parce que Python utilise Timsort qui est une sorte d'algorithme de tri adaptatif basé sur Marge_sort et insertion_sort. et la complexité de temps est p>
O (nlogn) p> blockQuote>
Pour trier () en python. p>
C'est pourquoi trié () travaillant plus vite que vous êtes p>
La solution de l'OP est O (n), cela n'explique donc pas pourquoi trié code> est plus rapide.
Intégré de Python Alors ... à la fois l'intégré Trié CODE> Fonction, qui utilise Timsort , est également
[- 5, -4, -2, -1] code> devient
[25, 16, 4, 1] code>. Timsort détecte cela et renverse simplement la liste. Temps O (n) à nouveau. Li>
trié code> et votre propre tri est O (n). Mais
trié code> est implémenté au niveau C, ce qui est plus rapide et a accès à des raccourcis, ce qui lui donne une constante cachée plus petite. P>
Oh, Duh, j'ai oublié de trier les listes d'entrée.
Corrigé et posté, trié code> est vraiment systématiquement plus rapide.
La Time LeetCode Rapports est la somme sur tous les cas de test que votre solution a été testée. Ceci est très peu fiable comme une mesure d'efficacité algorithmique, car
voir Cette réponse pour plus de discussion. p>
donc si si Nous voulons comparer les performances des deux algorithmes, il vaut mieux le faire nous-mêmes. J'ai fait; Voici mon temps pour les listes de taille pouvant atteindre 5 000 personnes (moyenne sur 1 000 points chacune): p>
Voici mon temps pour les listes de listes de taille jusqu'à 1 000 000 (pas de moyenne de moyenne): p>
Comme vous pouvez le constater, en utilisant Mon code de synchronisation est ci-dessous. Les temps ont été mesurés à l'aide de Python 3.5.2 et je reçois des résultats similaires avec Python 3.8.1. P> trié code> est toujours plus rapide Pour les petites et grandes entrées, la comparaison de Leetcode est donc correcte cette fois-ci (bien que peut-être juste par coïncidence quand même!). Votre solution est en effet linéaire et ces résultats sont compatibles avec cela. Mais la solution utilisant
trié code> semble également prendre du temps linéaire, et la réponse de Wasp Overflow explique pourquoi elle devrait. P>
def linear(A):
start = 0
end = len(A)-1
B = []
while start <= end:
if abs(A[start]) >= abs(A[end]):
B.append(A[start] * A[start])
start += 1
else:
B.append(A[end] * A[end])
end -= 1
B.reverse()
return B
def using_sorted(A):
return sorted(x*x for x in A)
from timeit import timeit
from random import randint
print('n', 'linear', 'using sorted', sep='\t')
for n in range(10000, 1000001, 10000):
data = sorted(randint(-10*n, 10*n) for i in range(n))
t1 = timeit(lambda: linear(data), number=1)
t2 = timeit(lambda: using_sorted(data), number=1)
print(n, t1, t2, sep='\t')
J'ai couru votre code et obtenu des résultats similaires, bien qu'ils ont terminé avec environ 0,95 et 0,40 seconde, la différence est donc plus forte pour moi. J'utilise 3.8.1, Leetcode utilise 3,8 , et je pense que quelque part environ 3,6 ou 3.7, ils ont amélioré code> de manière significative code> en utilisant des comparaisons plus rapides si toutes les valeurs sont des chiffres du même type.
Oui, était en 3.7: " trié () et list.sort () ont été optimisés pour Les cas communs doivent être atteints jusqu'à 40-75% plus rapidement.
Quelle est votre contribution?
Juste un FYI: L'algorithme décède sur l'entrée
[1,3,2] code> déjà. "O (n) tri" est toujours méfiant.
@tevemadar Le tableau d'entrée est trié dans ce problème, mais oui, devrait certainement avoir joint la définition de problème.
Sinon: la chose est que Python n'est pas rapide. Il est rapide lorsqu'il s'agit de câblage ensemble des bibliothèques implémentées dans le code natif. Et le tri intégré est une fuselorte hautement optimisée, implémentée en C, voir github.com/pythron/cpython/blob/master/Objects/listsort.txt pour sa description élaborée, et la mise en œuvre commence à github.com/pythron/cpytthon/blob/master/Objects/... . Edit: comme @davidl. J'ai révélé que l'entrée est triée, il peut être utile d'ajouter que le tri lié est dite extrêmement bien sur des listes partiellement triées.
Merci pour vos réponses, j'aurais dû poster la description du problème plus précisions. La liste d'entrée enritique doit être la raison pour laquelle le tri () est plus rapide. Le juge en ligne aurait dû tester la situation des limites ou une longue liste, donc je ne pense pas que le CNLG (N) avec relativement petit C est le cas ici.
Ne pensez jamais trop de réflexion sur les résultats de référence de la sous-5 secondes que vous n'avez pas de raison de faire confiance à être significatif. Vous pouvez faire sans le
inverse () code>: recherchez la limite entre les numéros négatifs et positive et procédez sur "INSID-OUT" - voir La réponse de Tevemadar pour faire sans
ABS () code>.
Voir aussi: triés carrés de nombres dans une liste dans O (n)? .