Comment récupérer les deux éléments les plus importants d'une liste contenant 100 000 entiers sans avoir à trier d'abord la liste complète?
En Python, utilisez heapq.nlargest
. Cette approche est la plus flexible au cas où vous souhaiteriez gérer davantage que les deux éléments les plus importants.
Voici un exemple.
>>> import heapq
>>> import random
>>> x = range(100000)
>>> random.shuffle(x)
>>> heapq.nlargest(2, x)
[99999, 99998]
Documentation: http://docs.python.org/library/heapq.html#heapq.nlargest
La réponse de JacobM est absolument la voie à suivre. Cependant, il faut garder à l’esprit quelques points lors de la mise en œuvre de ce qu’il a décrit. Voici un petit didacticiel qui vous guidera à travers les étapes les plus difficiles de la résolution de ce problème.
Si ce code est destiné à une utilisation en production, veuillez utiliser l’une des réponses les plus efficaces/concises de la liste. Cette réponse s'adresse à quelqu'un de nouveau en programmation.
L'idée est simple.
largest
et second_largest
.largest
, affectez-le à largest
.second_largest
, mais inférieur à largest
, affectez-le à second_largest
.Commençons.
def two_largest(inlist):
"""Return the two largest items in the sequence. The sequence must
contain at least two items."""
for item in inlist:
if item > largest:
largest = item
Elif largest > item > second_largest:
second_largest = item
# Return the results as a Tuple
return largest, second_largest
# If we run this script, it will should find the two largest items and
# print those
if __== "__main__":
inlist = [3, 2, 1]
print two_largest(inlist)
Ok, nous avons maintenant la réponse de JacobM en tant que fonction Python. Que se passe-t-il lorsque nous essayons de l'exécuter?
Traceback (most recent call last):
File "twol.py", line 10, in <module>
print two_largest(inlist)
File "twol.py", line 3, in two_largest
if item > largest:
UnboundLocalError: local variable 'largest' referenced before assignment
Apparemment, nous devons définir largest
avant de commencer la boucle. Cela signifie probablement que nous devrions également définir second_largest
.
Définissons largest
et second_largest
à 0.
def two_largest(inlist):
"""Return the two largest items in the sequence. The sequence must
contain at least two items."""
largest = 0 # NEW!
second_largest = 0 # NEW!
for item in inlist:
if item > largest:
largest = item
Elif largest > item > second_largest:
second_largest = item
# Return the results as a Tuple
return largest, second_largest
# If we run this script, it will should find the two largest items and
# print those
if __== "__main__":
inlist = [3, 2, 1]
print two_largest(inlist)
Bien. Lançons-le.
(3, 2)
Génial! Maintenant, testons avec inlist
étant [1, 2, 3]
inlist = [1, 2, 3] # CHANGED!
Essayons.
(3, 0)
... Euh oh.
La plus grande valeur (3) semble correcte. La deuxième plus grande valeur est cependant complètement fausse. Que se passe-t-il?
Travaillons à travers ce que la fonction fait.
largest
est 0 et second_largest
est également 0.largest
devient 1.largest
devient 2.Mais qu'en est-il de second_largest
?
Lorsque nous affectons une nouvelle valeur à largest
, la plus grande valeur devient la deuxième plus grande. Nous devons montrer cela dans le code.
def two_largest(inlist):
"""Return the two largest items in the sequence. The sequence must
contain at least two items."""
largest = 0
second_largest = 0
for item in inlist:
if item > largest:
second_largest = largest # NEW!
largest = item
Elif largest > item > second_largest:
second_largest = item
# Return the results as a Tuple
return largest, second_largest
# If we run this script, it will should find the two largest items and
# print those
if __== "__main__":
inlist = [1, 2, 3]
print two_largest(inlist)
Lançons-le.
(3, 2)
Fantastique.
Essayons maintenant avec une liste de nombres négatifs.
inlist = [-1, -2, -3] # CHANGED!
Lançons-le.
(0, 0)
Ce n'est pas juste du tout. D'où viennent ces zéros?
Il s'avère que les valeurs de départ pour largest
et second_largest
étaient en réalité plus grandes que tous les éléments de la liste. La première chose que vous pourriez envisager est de définir largest
et second_largest
sur les valeurs les plus basses possibles en Python. Malheureusement, Python n'a pas la plus petite valeur possible. Cela signifie que, même si vous définissez les deux comme -1 000 000 000 000 000 000, vous pouvez avoir une liste de valeurs plus petites que celle-là.
Alors, quelle est la meilleure chose à faire? Essayons de définir largest
et second_largest
aux premier et deuxième éléments de la liste. Ensuite, pour éviter le double comptage des éléments de la liste, nous ne regarderons que la partie de la liste située après le deuxième élément.
def two_largest(inlist):
"""Return the two largest items in the sequence. The sequence must
contain at least two items."""
largest = inlist[0] # CHANGED!
second_largest = inlist[1] # CHANGED!
# Only look at the part of inlist starting with item 2
for item in inlist[2:]: # CHANGED!
if item > largest:
second_largest = largest
largest = item
Elif largest > item > second_largest:
second_largest = item
# Return the results as a Tuple
return largest, second_largest
# If we run this script, it will should find the two largest items and
# print those
if __== "__main__":
inlist = [-1, -2, -3]
print two_largest(inlist)
Lançons-le.
(-1, -2)
Génial! Essayons avec une autre liste de nombres négatifs.
inlist = [-3, -2, -1] # CHANGED!
Lançons-le.
(-1, -3)
Attends quoi?
Passons à nouveau dans notre logique.
largest
est défini sur -3second_largest
est mis à -2Attends là. Déjà, cela semble faux. -2 est plus grand que -3. Est-ce ce qui a causé le problème? Nous allons continuer.
largest
est défini sur -1; second_largest
est défini sur l'ancienne valeur de largest
, qui est -3Oui, cela semble être le problème. Nous devons nous assurer que largest
et second_largest
sont correctement définis.
def two_largest(inlist):
"""Return the two largest items in the sequence. The sequence must
contain at least two items."""
if inlist[0] > inlist[1]: # NEW
largest = inlist[0]
second_largest = inlist[1]
else: # NEW
largest = inlist[1] # NEW
second_largest = inlist[0] # NEW
# Only look at the part of inlist starting with item 2
for item in inlist[2:]:
if item > largest:
second_largest = largest
largest = item
Elif largest > item > second_largest:
second_largest = item
# Return the results as a Tuple
return largest, second_largest
# If we run this script, it will should find the two largest items and
# print those
if __== "__main__":
inlist = [-3, -2, -1]
print two_largest(inlist)
Lançons-le.
(-1, -2)
Excellent.
Alors, voici le code, joliment commenté et formaté. Il a également eu tous les insectes que je pouvais trouver vaincu. Prendre plaisir.
Cependant, en supposant qu'il s'agisse vraiment d'une question de devoir, j'espère que vous obtiendrez une expérience utile en voyant un morceau de code imparfait lentement amélioré. J'espère que certaines de ces techniques seront utiles lors de futures missions de programmation.
Pas très efficace. Mais dans la plupart des cas, ça devrait aller: sur mon ordinateur (Core 2 Duo), une liste de 100 000 éléments peut être traitée en 0,27 seconde (en utilisant timeit
, moyennée sur 100 exécutions).
Vous parcourez la liste en conservant les variables contenant la valeur de l'élément le plus élevé et du deuxième élément le plus élevé rencontrées jusqu'à présent. Chaque nouvel élément rencontré remplacera celui des deux que le nouvel élément est supérieur à (le cas échéant).
Une méthode très simple consiste à utiliser heapq
. Heapify le tableau (O (n)), puis il suffit de faire apparaître plusieurs éléments dont vous avez besoin (log (n)). (Vu cette question dans une interview une fois, bonne question à garder à l'esprit.)
"2 plus haut" est impossible; un seul élément peut être "le plus élevé". Peut-être que vous voulez dire "plus haut 2". Dans tous les cas, vous devez indiquer quoi faire lorsque la liste contient des doublons. Que voulez-vous dans [8, 9, 10, 10]: (10, 9) ou (10, 10)? Si votre réponse est (10, 10), veuillez considérer les entrées de [8, 9, 10, 10, 10]. Qu'allez-vous faire avec les "deux meilleurs" quand vous les aurez? Veuillez modifier votre question pour donner ces indications.
En attendant, voici une réponse qui adopte la première approche (deux valeurs uniques):
largest = max(inlist)
second_largest = max(item for item in inlist if item < largest)
Vous devez ajouter des gardes pour moins de 2 valeurs uniques dans la liste.
Copiez votre List
dans List_copy
. Récupérez la valeur la plus élevée et obtenez sa position en:
Highest_value = max(List_copy)
Highest_position = List_copy.index(max(List_copy))
Attribuez 0
au Highest_value
.
List_copy[Highest_position] = 0
Et relancez votre ligne.
Second_Highest = max(List_copy)
Cela fonctionnera, mais je ne sais pas si vous souhaitez conserver les éléments de la liste:
max1 = max(myList)
myList.remove(max1)
max2 = max(myList)
Si vous le faites, vous pouvez faire ceci:
max1 = max(myList)
idx1 = myList.index(max1)
myList.pop(idx1)
max2 = max(myList)
myList.insert(idx1,max1)
Triez la liste et si liste n’est pas nulle, extrayez les deux derniers éléments.
>>> a=[0,6,8,5,10,5]
>>> a.sort()
>>> a
[0, 5, 5, 6, 8, 10]
>>> if a:
... print a[-1],a[-2]
...
10 8
Simple et efficace :)
Maintenant, si le tri n'est pas nécessaire, recherchez max, supprimez max, retrouvez max
>>> a=[0,6,8,5,10,5]
>>> max(a)
10
>>> a.remove(max(a))
>>> max(a)
8
>>>
Bien sûr, vous perdrez la liste d'origine, mais vous pouvez également créer une liste temporaire.
Le meilleur moment auquel vous pouvez vous attendre est linéaire, car vous devez au moins examiner tous les éléments.
Voici mon pseudocode pour résoudre le problème:
//assume list has at least 2 elements
(max, nextMax) = if (list[0] > list[1])
then (list[0], list[1])
else (list[1], list[0])
for (2 <= i < length) {
(max, nextMax) = if (max < list[i]) => (list[i], max)
elseif (nextMax < list[i]) => (max, list[i])
else (no change) => (max, nextMax)
}
return (max, nextMax)
Itérer dans toute la liste est le seul moyen de le faire sans trier.
Sans trier la liste, la seule façon de le faire consiste à parcourir la liste entière et à enregistrer les deux nombres les plus élevés. Je pense que vous feriez mieux de trier la liste.
Une autre solution qui utilise uniquement les fonctions de base Python peut être vue ci-dessous:
>>> largest = max(lst)
>>> maxIndex = lst.index(largest)
>>> secondLargest = max(max(lst[:maxIndex]), max(lst[maxIndex+1:]))
Si nous divisons une liste autour de son plus grand nombre, nous savons que le deuxième plus grand nombre se trouve soit dans la moitié gauche, soit dans la moitié droite. Donc, nous pouvons trouver trivialement le deuxième plus grand nombre en cherchant simplement le plus grand du plus grand nombre dans les moitiés gauche et droite de la liste.
Il est trivial de montrer que c'est O(n) time et O(1) space. Nous parcourons la liste une fois pour trouver le plus grand élément, puis de nouveau pour trouver le deuxième plus grand. Nous ne stockons que les valeurs les plus grandes et l'indice de la valeur la plus grande.
Le deuxième élément le plus élevé est un cas assez simple, mais pour le k e élément le plus élevé, vous voulez un algorithme de sélection . Cette page est assez complète, il est donc probablement préférable de la lire.
Je sais que ce sujet est ancien, mais voici une solution simple à ce problème. Testé contre heapq.nlargest et ceci est un peu plus rapide (aucun tri nécessaire):
Fonctionne pour les nombres positifs et négatifs.
Fonction ci-dessous: Temps maximal utilisé: 0,12, mémoire maximale utilisée: 29290496 Heapq.nlargest: Temps maximal utilisé: 0,14, mémoire maximale: 31088640
def two_highest_numbers(list_to_work):
first = None
second = None
for number in list_to_work:
if first is None:
first = number
Elif number > first:
second = first
first = number
else:
if second is None:
second = number
Elif number > second:
second = number
return [first, second]