IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Algorithmes et structures de données Discussion :

Trouver des paires et des triplets dans une liste


Sujet :

Algorithmes et structures de données

  1. #1
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut Trouver des paires et des triplets dans une liste
    Hello,

    On m'a récemment poser une question intéressante (pour une fois ) lors d'un entretien d'embauche :
    Comment trouver des paires dans une liste de nombres et quelle est la complexité ?
    Exemple : Dans la liste [1, 1, 1, 2, 4, 2, 2, 2]
    Résultats : 3 paires (1, 2, 2), il y a 3 '1' -> on peut faire qu'une seule paire avec et 4 '2' -> 2 paires de '2'

    Il y a une solution en O(n), on ajoute tous les nombres dans une hash map, et pour chaque nombre on divise le nombre d’occurrences par deux.
    code (C++)
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    std::unordered_map<int, int> hm;
    auto nbs = {1, 1, 1, 2, 4, 2, 2, 2};
    for(auto n: nbs) {
       ++hm[n];
    }
    int nbPairs = 0;
    for(auto const& kv: hm) {
       nbPairs += (kv.second / 2);
    }
    std::cout << nbPairs << std::endl;

    Puis, puis même question pour trouver des triplets.
    Exemple : Dans la liste [1, 1, 1, 2, 4, 2, 2, 2]
    Résultats : 2 triplets (1, 2), il y a 3 '1' -> 1 triplet; 4 '2' -> 1 triplet

    Sur le coup, j'ai donné un algo en O(n²), qui en y réfléchissant n'a pas de sens : appliquer 2 fois l'algo précédent de manière imbriqué. La complexité en O(n²) que j'ai donné n'a pas choqué, l'algo par contre ... :p
    Alors que le même algo en O(n) semble marcher :
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    std::unordered_map<int, int> hm;
    auto nbs = {1, 1, 1, 2, 4, 2, 2, 2};
    for(auto n: nbs) {
       ++hm[n];
    }
    int nbPairs = 0;
    for(auto const& kv: hm) {
       nbPairs += (kv.second / 3);
    }
    std::cout << nbPairs << std::endl;

    Du coup ma question : Si une complexité en O(n²) ne choque pas, c'est qu'il y a un algo (probablement naïf); Mais j'arrive pas à trouver, une idée ?

  2. #2
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    salut,

    un peu au pif, pour chaque valeur du tableau parcourir l'intégralité des valeurs restantes ?

  3. #3
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Hum,

    Ça pose pas de problème avec les triplets détectés plusieurs fois ?
    Pour '2', il serait détecté 2 fois :
    - Itération sur le 1er '2' : [4, 2, 2, 2] -> 1 paire, + 1 triplet
    - Itération sur le 2eme '2' : [2, 2] -> 1 paire, + 1 triplet
    => 2 est compté comme 2 triplets.

    Quelque chose comme ça marcherait ?
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    auto nbs = { 1, 1, 1, 2, 4, 2, 2, 2 };
    int nbPairs = 0;
    for (auto b = nbs.begin(), e = nbs.end(); b != e; ++b) {
    	int n = 0;
    	for (auto it = b + 1; it != e; ++it) {
    		if (*it == *b) {
    			++n;
    		}
    	}
    	nbPairs += n / 2;
    }
    nbPairs /= 3;
    ++nbPairs;
    std::cout << nbPairs << std::endl;

    Division du nombre de pairs par 3 + 1 à la fin pour corriger la détection multiple. C'est correct ?

  4. #4
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 459
    Points
    13 459
    Par défaut
    Bonjour

    J'ai rien compris.
    Tu comptes les occurrences pour chaque motif et tu divises par 2 pour les couples, par 3 pour les triplets et par 6 pour les hexuplets.
    Mais tu trouves ça trop simple. Alors tu viens ici demander plus compliqué ?

    Euh ... pourquoi faire compliqué ?

    Et puisqu'on se balance du code à la figure, tiens, prends

    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ cat triplets.txt 
    1
    1
    1
    2
    4
    2
    2
    2 
    $ awk '{a[$1]++;} END{for (i in a) print i,"->",int(a[i]/2),"couple(s)";}' triplets.txt                                                                                                              
    4 -> 0 couple(s)
    1 -> 1 couple(s)
    2 -> 2 couple(s)
    $ awk '{a[$1]++;} END{for (i in a) print i,"->",int(a[i]/3),"triplet(s)";}' triplets.txt                                                                                                             
    4 -> 0 triplet(s)
    1 -> 1 triplet(s)
    2 -> 1 triplet(s)
    $ awk '{a[$1]++;} END{for (i in a) print i,"->",int(a[i]/6),"hexuplet(s)";}' triplets.txt 
    4 -> 0 hexuplet(s)
    1 -> 0 hexuplet(s)
    2 -> 0 hexuplet(s)
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  5. #5
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Flodelarab Voir le message
    Mais tu trouves ça trop simple. Alors tu viens ici demander plus compliqué ?

    Euh ... pourquoi faire compliqué ?
    Par simple curiosité, une solution existe en O(n²), je voyais pas trop comment.

  6. #6
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Ça pose pas de problème avec les triplets détectés plusieurs fois ?
    ben l'idée c'est de "retirer" (pop ?) les valeurs du tableau au fur et à mesure, nécessairement

    alors en C++ flemme mais en python (2.7) ça donnerait un truc comme ça très naïvement :
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # -*- coding: utf8 -*-
    L = [1, 1, 1, 2, 4, 2, 2, 2]
    paires = []
    while True:
       current = L[0]
       L.pop(0)  # reste le tableau moins le premier élément qui sert de référence (current)
       for i in range(len(L)): # on parcourt tout le tableau restant donc
          if L[i] == current:
             L.pop(i)  # on retire du tableau l'élément appairé via son indice
             paires.append(current) # et on ajoute l'élément dont on a trouvé la paire dans une autre liste, pour affichage à la fin...
             print '- paire de %d trouvée, reste %s' % (current, str(L))
             break
       if len(L) < 2: # s'il reste 0 ou 1 élément dans le tableau on ne peut plus trouver de paire, donc on sort
          break
    print '%d paires trouvées %s' % (len(paires), str(paires))

    et le résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    - paire de 1 trouvée, reste [1, 2, 4, 2, 2, 2]
    - paire de 2 trouvée, reste [4, 2, 2]
    - paire de 2 trouvée, reste []
    3 paires trouvées [1, 2, 2]

  7. #7
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 057
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 057
    Points : 9 396
    Points
    9 396
    Par défaut
    Et pourquoi pas trier la liste ( avec un algo en n*log(n) )
    Puis ensuite parcourir le résultat avec une boucle de ce genre


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
    Procedure recherche(Ma_liste_triee  , n = 3 )        // n = 2 pour la recherche de couples , n=3 pour la recherche de triplets , etc.
    t = taille(ma_liste_triee)
    n_uplets = 0
    i=0      // ou i = 1 si les éléments sont numérotés de 1 a t
    tantque i +n-1 < t      
      si   ma_liste_triee[i] = ma_liste_triee[i+ n-1] alors 
          n_uplets ++
          i+= n
      sinon
          i++
      fin
    fin
    renvoyer n_uplets
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  8. #8
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Points : 6 498
    Points
    6 498
    Par défaut
    Si on est sur d'avoir des entiers, Pourquoi pas la méthode du tri du panier, un premier parcours pour trouver le plus grand, nombre, on cree le tableau , on parcours la liste pour compter les occurences opuis le tableau résultat. En gros O(3 * n)
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  9. #9
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    @BufferBob : ça à l'air d'être ça, merci.

    @tbc92 : c'est une idée, mais on est pas en O(n²).

    @Trap D : Toujours pas en O(n²).

  10. #10
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par Trap D Voir le message
    Si on est sur d'avoir des entiers, Pourquoi pas la méthode du tri du panier, un premier parcours pour trouver le plus grand, nombre, on cree le tableau , on parcours la liste pour compter les occurences opuis le tableau résultat. En gros O(3 * n)
    Le bucket sort ? Mais il faut en plus que les entiers soient petits. A ce compte-là pas besoin de trier, il suffit d'un tableau de compteurs.

    Plus généralement on utilise un sac (multiset) : un ensemble dans lequel on compte le nombre d'instances de chaque élément. Comme toutes les structures communes basées sur des hashes, les temps d'accès sont en O(1).

    Donc O(n).

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 9
    Dernier message: 26/03/2011, 09h46
  2. liste récursive des fichiers d'un dossier dans une liste
    Par identifiant_bidon dans le forum Langage
    Réponses: 2
    Dernier message: 30/06/2010, 16h33
  3. scanner des dossiers et les mettre dans une liste
    Par niklos0 dans le forum Programmation et administration système
    Réponses: 1
    Dernier message: 08/09/2008, 14h34
  4. Réponses: 4
    Dernier message: 11/07/2007, 19h28
  5. Réponses: 4
    Dernier message: 24/04/2003, 22h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo