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

C++ Discussion :

Acc�s � un ensemble ordonn� sur l'insertion


Sujet :

C++

  1. #1
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut Acc�s � un ensemble ordonn� sur l'insertion
    Bonjour,

    Je cherche � utiliser un ensemble ordonn� sur l'insertion des �l�ments qui poss�de trois types d'acc�s :
    - acc�s sur le plus ancien en o(1)
    - acc�s sur le plus r�cent en o(1)
    - acc�s al�atoire en o(log(n)) (pas forc�ment indiciel)
    Avec n grand.

    Je pensais utiliser un AVL mais je voulais savoir si vous connaissiez une autre m�thode plus pratique, je ne cherche pas � recr�er la roue Une sorte de tas peut-�tre ?

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    D�tails du profil
    Informations personnelles :
    Localisation : France, Paris (�le de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par d�faut
    Salut !

    Ta question n'est pas tr�s claire, que veux-tu dire par "acc�s al�atoire en o(log(n)) (pas forc�ment indiciel)" ? Quelle est la cl� d'acc�s dans ce mode ?

    Si la relation d'ordre est l'ordre d'insertion et que tes contraintes ne concernent que l'acc�s, un simple std::vector fera l'affaire et sera meilleur que O(log(n)) en acc�s.

    Si tu dois rechercher les �l�ments par cl�, un std::unordered_map fera l'affaire et sera meilleur que O(log(n)).

    Si ta relation d'ordre est sur la cl� de recherche, c'est un std::map qu'il te faut.

    Si j'ai bien compris ta question, il te faut une combinaison de std::vector (pour l'ordre) et de std::unordered_map (pour la recherche). Si tu dois supporter la suppression, utiliser une std::list en lieu et place d'un std::vector peut s'av�rer judicieux. Si c'est bien ce que tu recherches mais que tu ne vois pas comment utiliser ces �l�ments pour l'impl�menter proprement, nous pourrons t'aider .

  3. #3
    Expert �minent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Salut,

    Le plus facile pour avoir un acc�s en O(log(n)) sur n'importe quel �l�ment, c'est sans doute d'utiliser la classe std::set, � moins que tu ne veuilles pas de l'unicit� des �l�ments (ce n'est pas un AVL, mais un Red Black Tree, ce qui revient grosso modo au m�me ).

    Il ne resterait plus qu'� assurer l'acc�s � l'�l�ment le plus r�cent et � l'�l�ment le plus ancien, et pour ca, il y a une solution simple qui ne permet pas de supprimer un �l�ment et une solution � peine moins simple qui pourrait m�me te permettre de supprimer des �l�ment tout en �tant sur de toujours obtenir le bon r�sultat.

    La solution la plus simple pourrait prendre une forme proche de

    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    template <typename T>
    class MyContainer{
    public:
        using const_iterator = typename std::set<T>::const_iterator;
        MyContainer():firstInserted_(datas_.end()), lastInserted_(datas_.end()){}
        void insert(T const & t){
            auto it = datas_.insert(t);
           /* si l'insertion réussi */
            if( it.second){
                /* si on n'a pas encore défini d'itérateur valide pour le premier élément */
                if (firstInserted_ == datas_.end())
                    firstInserted_= it.first; // c'est que l'itérateur récupéré est le premier élément inséré
                /* et c'est de toutes façons aussi le dernier élément inséré */
                lastInserted_=it.first;
            }
        }
        const_iterator firstInserted()const{return firstInserted_;}
        const_iterator lastInserted() const{return lastInserted_;}
        const_iterator begin() const{return datas_.begin();}
        const_iterator end() const{return datas_.end();}
        size_t size() const{return datas_.size();}
        /* l'accès en O(log(n)) se fait ici */
        const_iterator find(T const & t) const{return datas_.find(t);}
    private:
        std::set<T> datas_;
        const_iterator firstInserted_;
        const_iterator lastInserted_;
    };
    et elle pourrait �tre utilis�e sous une forme proche de
    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
    16
    17
    18
    19
    int main()
    {
        MyContainer<int> obj;
        obj.insert(1);
        std::cout<<"first :"<<*(obj.firstInserted())
                 <<" last :"<<*(obj.lastInserted())<<std::endl;
     
        obj.insert(2);
        obj.insert(3);
        std::cout<<"first :"<<*(obj.firstInserted())
                 <<" last :"<<*(obj.lastInserted())<<std::endl;
        std::cout<<"all :"<<std::endl;
        size_t count =1;
        for(auto const & it : obj){
            std::cout<<count<<" : "<<it<<std::endl;
            ++count;
        }
        return 0;
    }
    [EDIT]Je me suis gouru de bouton, je voulais pr�visualiser ma r�ponse.

    Si tu veux aussi pouvoir g�rer la suppression d'�l�ments, il va falloir un tout petit peu plus de r�flexion.

    On ne peut en effet pas se contenter de maintenir le premier et le dernier �l�ment rajout�, tout simplement parce qu'on ne peut absolument pas faire la moindre pr�somption sur l'ordre dans lequel les �l�ments seront supprim�s.

    A chaque fois que l'utilisateur d�cidera de supprimer un �l�ment, il risque de supprimer soit le premier �l�ment qui a �t� ajout�, soit le dernier, soit... n'importe quel �l�ment se trouvant entre les deux (dans l'ordre d'insertion, s'entend).

    Nous devons donc trouver le moyen de garder une trace de l'ordre dans lequel les diff�rents �l�ments ont �t� rajout�s. Et vu qu'il faut que l'acc�s au premier et au dernier �l�ment se fasse en temps constant, la collection qui nous permettra de le faire est toute trouv�e : il nous faut une liste doublement chain�e (note qu'une liste simplement chain�e pourrait sans doute �galement suffire ). Ca tombe bien, le standard nous en propose une avec la classe template deque!

    L'id�e est alors que, pour chaque �l�ment ajout�, nous allons ajouter l'it�rateur vers l'�l�ment rajout� � la fin d'une liste doublement chain�e et que, chaque fois que nous voudrons retirer un �l�ment, nous veillerons � le supprimer aussi bien de la liste que du set.

    Cela pourrait prendre la forme de
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    template <typename T>
    class MyContainer{
    public:
        using const_iterator = typename std::set<T>::const_iterator;
        MyContainer(){}
        void insert(T const & t){
            auto it = datas_.insert(t);
           /* si l'insertion réussi */
            if( it.second){
                /* on rajoute l'itérateur à la liste */
                inserted_.push_back(it.first);
            }
        }
        const_iterator firstInserted()const{return *(inserted_.begin());}
        const_iterator lastInserted() const{return *(inserted_.rbegin());}
        const_iterator begin() const{return datas_.begin();}
        const_iterator end() const{return datas_.end();}
        size_t size() const{return datas_.size();}
        void erase(const_iterator item){
            /* si l'élément que l'on s'apprête à supprimer est valide
             */
            if(item!= datas_.end()){
                /* recherchons l'élément dans la liste représentant 
                 * l'ordre d'insertion
                 */
                auto it = inserted_.begin();
                while(*(it) != item && it!= inserted_.end())
                    ++it;
                /* si on a trouvé l'élément dans la liste (cela
                 * sera normalement toujours le cas, vu que l'élément
                 * à supprimer est valide)
                 */
                if(it != inserted_.end()){
                    /* alors, on le supprime de la liste */
                    inserted_.erase(it);
                }
                /* et on le supprime du set */
                datas_.erase(item);
            }
        }
    private:
        std::set<T> datas_;
        std::deque<const_iterator> inserted_;
    };
    que nous pourrions utiliser sous la forme de
    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
    16
    17
    18
    19
    20
    21
    22
    23
    int main()
    {
        MyContainer<int> obj;
        obj.insert(1);
        std::cout<<"first :"<<*(obj.firstInserted())
                 <<" last :"<<*(obj.lastInserted())<<std::endl;
     
        obj.insert(2);
        obj.insert(3);
        std::cout<<"first :"<<*(obj.firstInserted())
                 <<" last :"<<*(obj.lastInserted())<<std::endl;
        std::cout<<"all :"<<std::endl;
        size_t count =1;
        for(auto const & it : obj){
            std::cout<<count<<" : "<<it<<std::endl;
            ++count;
        }
        std::cout<<"erasing "<<*(obj.firstInserted())<<std::endl;
        obj.erase(obj.firstInserted());
        std::cout<<"first :"<<*(obj.firstInserted())
                 <<" last :"<<*(obj.lastInserted())<<std::endl;
        return 0;
    }
    En fait, je disais au d�but que cette solution �tait un peu plus compliqu�e. Disons qu'elle m�ritait un peu plus de r�flexion, mais le code qui permet d'obtenir le r�sultat escompt� n'est pas forc�ment plus compliqu�

    NOTA: la syntaxe que j'ai utilis�e ici utilise les possibilit�s de C++11. La transformation du code pour respecter l'ancienne norme (celle d'avant 2011 ) n'est pas vraiment compliqu�e. Mais, si tu as un probl�me pour faire la transformation, tu sais o� t'adresser
    A m�diter: La solution la plus simple est toujours la moins compliqu�e
    Ce qui se con�oit bien s'�nonce clairement, et les mots pour le dire vous viennent ais�ment. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 f�vrier 2014
    mon tout nouveau blog

  4. #4
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut
    Ta question n'est pas tr�s claire, que veux-tu dire par "acc�s al�atoire en o(log(n)) (pas forc�ment indiciel)" ? Quelle est la cl� d'acc�s dans ce mode ?
    Mes excuses, je pr�cise.
    Une premi�re approche serait de retourner un �l�ment al�atoirement de l'ensemble selon une loi uniforme approch�e.
    Une seconde approche serait de retourner le k i�me �l�ment ins�r� dans l'ensemble.
    La cl� d'acc�s n'est pas l'�l�ment mais l'ordre dans lequel on insert les �l�ments dans l'ensemble. Cette cl� d'acc�s n'existe donc pas pour l'utilisateur dans la premi�re approche. Dans la deuxi�me approche, je voudrais retourner le ki�me ins�rer parmi n �l�ments de l'ensemble.

    Un std::vector serait parfait sauf que pour supprimer un �l�ment je suis oblig� de tout d�cal�(comportement par d�faut), au lieu de faire un swap et de supprimer le dernier, pour conserver l'ordre d'insertion.

    Je ne pense pas pouvoir utiliser de cl� pour faire cela car il y aurait des probl�mes d'indice lors de la suppression.

    Je lis ta r�ponse koala01

  5. #5
    R�dacteur/Mod�rateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 38
    Localisation : Canada

    Informations professionnelles :
    Activit� : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Pensez � consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation r�seau ?
    Aucune aide via MP ne sera dispens�e. Merci d'utiliser les forums pr�vus � cet effet.

  6. #6
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut
    Le plus facile pour avoir un acc�s en O(log(n)) sur n'importe quel �l�ment, c'est sans doute d'utiliser la classe std::set, � moins que tu ne veuilles pas de l'unicit� des �l�ments (ce n'est pas un AVL, mais un Red Black Tree, ce qui revient grosso modo au m�me ).
    Je pense en effet que mon approche du probl�me n'est forc�ment la bonne. Simplifions le probl�me en disant que l'acc�s au plus ancien et au plus r�cent est contenu dans l'acc�s indiciel en log(n). Un std::set ou std::map utilise l'ordre sur l'�l�ment ou la cl�. Mais je ne comprends pas comment on peut l'utiliser dans ce cas-ci.

    Si je veux une m�thode my_set.get(i) par exemple avec n �l�ments, la m�thode find de set ou map prend seulement en compte une recherche qui appartient d'une fa�on ou d'une autre � mon arbre(soit l'objet soit la cl�).

    Or avec l'ordre d'insertion, je ne peux pas utiliser de cl� et je ne peux donc pas utiliser find().
    J'ai de fait deux questions :
    - pensez-vous qu'il existe une formule permettant de trouver le parcourt exacte dans un AVL pour atteindre l'�l�ment i sachant qu'on insert toujours par la droite ?
    - sinon, si en plus de la balance, je rajoute et maintient sur chaque noeud de l'AVL le nombre de fils gauche et droit, ce soit faisable ?


    Peut-�tre existe-t-il un type de ce genre d�j� impl�ment� ?

  7. #7
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut
    Citation Envoy� par Bousk Voir le message

    For operations that involve frequent insertion or removals of elements at positions other than the beginning or the end, deques perform worse and have less consistent iterators and references than lists and forward lists.
    Je cherche � pouvoir supprimer un �l�ment � n'importe quelle position.

  8. #8
    R�dacteur/Mod�rateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 38
    Localisation : Canada

    Informations professionnelles :
    Activit� : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par d�faut
    Pourrais-tu te mettre d'accord avec ton besoin ?
    D'abord,
    Je cherche � utiliser un ensemble ordonn� sur l'insertion des �l�ments qui poss�de trois types d'acc�s :
    - acc�s sur le plus ancien en o(1)
    - acc�s sur le plus r�cent en o(1)
    - acc�s al�atoire en o(log(n)) (pas forc�ment indiciel)
    Maintenant,
    Je cherche � pouvoir supprimer un �l�ment � n'importe quelle position.
    Et apr�s ?

    Si tu recherches la collection magique, je crains que tu ne trouves pas.
    Pensez � consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation r�seau ?
    Aucune aide via MP ne sera dispens�e. Merci d'utiliser les forums pr�vus � cet effet.

  9. #9
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    D�tails du profil
    Informations personnelles :
    Localisation : France, Paris (�le de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par d�faut
    Diable, ce que tu veux est plus difficile que pr�vu ! Bousk n'a pas tort, es-tu s�r de ne pas en vouloir trop ? Le fait que tu veuilles un acc�s en O(log(n)) me sugg�re que non car les collections qui supportent la recherche par indices sont bien meilleurs que O(log(n)). Tu es donc pr�t � sacrifier de la perf d'acc�s pour pouvoir supprimer proprement.

    Citation Envoy� par Kreatore Voir le message
    - pensez-vous qu'il existe une formule permettant de trouver le parcourt exacte dans un AVL pour atteindre l'�l�ment i sachant qu'on insert toujours par la droite ?
    - sinon, si en plus de la balance, je rajoute et maintient sur chaque noeud de l'AVL le nombre de fils gauche et droit, ce soit faisable ?
    Il semble en effet que ce soit faisable. Par contre, �a va te faire beaucoup de boulot pour avoir quelque chose de fiable, il te faudra �crire de bon tests. Ca serait mieux d'exploiter la STL mais l� je vois pas trop comment.

  10. #10
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut
    jblecanard, oui je suis pr�t � sacrifier de la performance, surtout que log(n) reste plut�t bon

    Le lien que tu m'as donn� � l'air int�ress�, je vais suivre cette piste m�me si la STL reste puissante et fiable, je ne trouve pas comment l'utiliser.

  11. #11
    Membre chevronn�

    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 36
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activit� : D�veloppeur informatique
    Secteur : Sant�

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Par d�faut
    Je n'ai pas regard� la complexit� des op�rations en d�tail, mais tu peux aussi regarder du c�t� de cet article qui d�crit une alternative � std::set. En rempla�ant std::vector par std::list dans l'impl�mentation, peut �tre que tu peux arriver � quelque chose qui se rapproche de ce que tu veux ?

  12. #12
    Membre averti
    Homme Profil pro
    �tudiant
    Inscrit en
    D�cembre 2013
    Messages
    13
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : D�cembre 2013
    Messages : 13
    Par d�faut
    Bonjour !

    Le probl�me de la liste c'est l'acc�s al�atoire et le probl�me du vector c'est la suppression(puisque je veux conserver l'ordre). La complexit� est importante pour la solution que je veux mettre en place. Je vais essayer de rassembler les besoins de cette collection (qui peut-�tre existe ) :
    - Les �l�ments sont tri�s dans l'ordre d'insertion
    - La diff�rence entre o(n) et o(logn) est importante pour cette collection.
    - L'acc�s al�atoire est critique.
    - Insertion et suppression sont aussi importante l'une que l'autre.

    Maintenant la piste que j'impl�mente :
    - AVL
    - Ajout du nombre de fils gauche repr�sentant alors directement l'indice du noeud

    Du coup pour un acc�s al�atoire il suffit si on va � gauche de prendre ce nombre et si on va � droite d'ajouter le nombre de n�uds laisser � gauche.

    Bref. Je sais pas si c'est tr�s compr�hensible. Le seul probl�me c'est que la taille de la collection est limit�e avec cette m�thode. Mais bon on avoisine sur 32 bits les quelques milliards et m�me presque le double ou plus ? Si un matheux � la r�ponse

    Sinon j'aimerais bien voir une impl�mentation simple d'AVL avec la gestion de la balance, si vous connaissez une source libre. J'ai r�ussi � faire les rotations et � r��quilibrer mais je n'ai aucune id�e pour faire cela de fa�on propre ni pour la suppression...

+ R�pondre � la discussion
Cette discussion est r�solue.

Discussions similaires

  1. probl�me sur requete insert
    Par shadowmoon dans le forum Langage SQL
    R�ponses: 2
    Dernier message: 09/06/2005, 11h46
  2. Violation d'acc�s dans l'EDI sur compo1 apres suppr de comp2
    Par RamDevTeam dans le forum Composants VCL
    R�ponses: 2
    Dernier message: 31/05/2005, 15h02
  3. Evenement sur UPDATE, INSERT, DELETE
    Par papouAlain dans le forum Langage SQL
    R�ponses: 6
    Dernier message: 23/12/2004, 14h58
  4. [LISTENER] sur l'insertion de cd
    Par divxdede dans le forum Multim�dia
    R�ponses: 2
    Dernier message: 03/07/2004, 11h28
  5. probleme d'acces a une machine sur un r�seau
    Par zorian dans le forum D�veloppement
    R�ponses: 3
    Dernier message: 09/06/2004, 13h04

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