web-dev-qa-db-fra.com

Récupérer les deux éléments les plus importants d'une liste contenant 100 000 entiers

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?

30
Joey

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

54
FogleBird

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

L'idée est simple.

  • Conservez deux variables: largest et second_largest.
  • Parcourez la liste.
    • Si un élément est supérieur à largest, affectez-le à largest.
    • Si un élément est supérieur à second_largest, mais inférieur à largest, affectez-le à second_largest.

Commencer

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.

Initialisation des variables

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.

Fixer la logique

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.

  • Lorsque nous commençons, largest est 0 et second_largest est également 0.
  • Le premier élément de la liste que nous examinons est 1, alors largest devient 1.
  • Le prochain élément est 2, alors 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.

Initialisation des variables, partie 2

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?

Initialisation des variables, partie 3

Passons à nouveau dans notre logique.

  • largest est défini sur -3
  • second_largest est mis à -2

Attends 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 -3

Oui, 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.

Conclusion

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.


Efficacité

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

15
Wesley

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

6
Jacob Mattison

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

5
zdav

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

2
John Machin

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)
1
Dmitry

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)
1
Jeff B

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.

0
NEERAJ VASHISTHA

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)
0
Phil

Itérer dans toute la liste est le seul moyen de le faire sans trier.

0
Fergal Moran

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.

0
jaltiere

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.

0
Shuklaswag

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. 

0
aspo

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]
0
user1980232