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 :

melange d'elements dans vector crée doublons


Sujet :

C++

  1. #1
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut melange d'elements dans vector crée doublons
    Bonjour,

    je stocke une jeu de cartes dans un vector, et j'ai écrit une méthode permettant de mélanger les cartes.


    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
    class Croupier
        {
            public :
     
                static std::vector<Carte> Paquet;
                static std::vector<Carte> Defausse;
     
                Croupier() {/*nothing to do */}
     
                ~Croupier() {/*nothing to do*/}
     
                void Melanger()
                {
                    int i = 0;
                    for (auto c : Paquet)
                    {
                        do
                        {
                            i = rand() % Paquet.size();
                        }while (Paquet.at(i) == c);
     
                        std::swap(c, Paquet.at(i));
                    }
                }
    dans le main :
    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
     
     
    std::vector<Carte> Jeu::Croupier::Paquet;
     
    int main()
    {
        //initialisation des textures des cartes
        Carte::InitTextureCartes();
        //initialisation de la graine
        srand((unsigned)time(NULL));
     
        //instanciation de la fenetre
        sf::RenderWindow App(sf::VideoMode(width,height,32), "testSFML");
     
        //instanciation des joueurs
        Joueurs::Joueur j1("Kamui"), j2("Alu"), j3("7"), j4("TeK");
     
        //instanciation du croupier
        Jeu::Croupier c;
     
        //le croupier choisit un jeu de 32 cartes
        c.Init_32_cartes();
     
        c.Melanger();
     
        Joueurs::Distribuer(Jeu::Croupier::Paquet);
        //[...]
    Apres c.init_32_cartes(); si je vérifie le contenu du vector rien à dire, tout est normal.

    Apres c.Melanger();, des doublons sont apparus et (forcément) des cartes ont disparu...

    Je cherche depuis un bon moment une explication maintenant, mais je ne trouve rien dans cet algo qui ne pose problème...

    (j'ai un petit doute sur ce que fait swap dans mon cas d'utilisation...)

    Vous avez une idée?
    Nullius in verba

  2. #2
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    j'ai suivi mon instinct et :

    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
     
    void Melanger()
                {
                    int i = 0;
                    int j = 0;
                    for (auto c : Paquet)
                    {
                        do
                        {
                            i = rand() % Paquet.size();
                        }while (i != j);
     
                        std::swap(Paquet.at(j), Paquet.at(i));
                        j++;
                    }
                }
    là je n'ai plus de doublons...

    Je ne comprends pas ce qui ne va pas avec le swap précédent...



    [HS]
    je viens de m'apercevoir aussi que les cartes affichées sont toujours les mêmes (erreur classique de gestion du rand), mais je pensais avoir fait exactement ce qu'il fallait (initialisaiton de la graine dans le main, pour que ce ne soit fait qu'une fois, et ensuite bah utilisation de rand...)
    [/HS]
    Nullius in verba

  3. #3
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,
    Avant toute chose, pour mélanger un paquet de carte, la manière la plus simple reste d'utiliser std::random_shuffle()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     void Melanger()
    {
       std::random_shuffle(Paquet.begin(), Paquet.end());
    }
    Mais sinon pour le problème initial, il vient de l'erreur classique de l'utilisation de la boucle range-based for (et qui je soupçonne entrera rapidement au hit parade des bugs les plus courant en C+11). Dans ce code :

    c est une carte temporaire, une copie d'une carte appartenant au Paquet donc std::swap(c, Paquet.at(i)); swap la carte numéro i avec une carte temporaire.
    Il faut écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    for (Carte& c : Paquet)
    si l'on veut une sémantique de référence.

    Personnellement je haie cette manie d'utiliser auto dans une boucle range-based for, une des raisons étant qu'auto masque le type réel et encourage ce genre d'erreur.

  4. #4
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Bonjour,
    Avant toute chose, pour mélanger un paquet de carte, la manière la plus simple reste d'utiliser std::random_shuffle()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     void Melanger()
    {
       std::random_shuffle(Paquet.begin(), Paquet.end());
    }
    Mais sinon pour le problème initial, il vient de l'erreur classique de l'utilisation de la boucle range-based for (et qui je soupçonne entrera rapidement au hit parade des bugs les plus courant en C+11). Dans ce code :

    c est une carte temporaire, une copie d'une carte appartenant au Paquet donc std::swap(c, Paquet.at(i)); swap la carte numéro i avec une carte temporaire.
    Il faut écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    for (Carte& c : Paquet)
    si l'on veut une sémantique de référence.

    Personnellement je haie cette manie d'utiliser auto dans une boucle range-based for, une des raisons étant qu'auto masque le type réel et encourage ce genre d'erreur.
    merci pour ta réponse, je vais suivre tes conseils. Pour l'utilisation (intempestive) de auto, c'est que j'ai lu qu'il était mieux de l'utiliser autant que possible, car cela améliore les performances.

    C'est vrai finalement ?

    J'ai toujours l'erreur basique "toujours le meme résultat après relance du programme", il faut faire quoi avec std::random_shuffle ?

    Edit : OK, il faut quand même le srand(time(NULL)); T'aurais pas un truc moins C-ien en stock ?
    Nullius in verba

  5. #5
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    merci pour ta réponse, je vais suivre tes conseils. Pour l'utilisation (intempestive) de auto, c'est que j'ai lu qu'il était mieux de l'utiliser autant que possible
    Oui, herb sutter par exemple conseille régulièrement de l'utilser dès que possible, perso ça me hérisse le poil et je ne vois pas ce qu'on gagne réellement à écrire for(auto& c : Paquet) au lieu de for(Carte& c : Paquet) (sauf que c'est plus clair qu'on manipule des Carte dans le deuxième cas)
    Citation Envoyé par Kaamui
    , car cela améliore les performances
    Non, ça c'est faux les performances sont exactement identique dans ce cas.
    Je pense que tu confonds avec qqchose du style :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    auto lambda = [] (){do_something();};
    std::function<void()> f = [] (){do_something();};
    Dans ce cas, en effet auto est plus performant vu qu'il permet de conserver le vrai type de la lambda, au lieu de devoir copier la lambda dans une std::fonction (donc avec potentiellement un new et un appel à une fonction virtuelle)

    Citation Envoyé par Kaamui Voir le message
    Edit : OK, il faut quand même le srand(time(NULL)); T'aurais pas un truc moins C-ien en stock ?
    Si tu es en C++11, il y a le nouveau header <random> qui permet de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void Melanger()
    {
       std::random_device rd;
       std::default_random_engine re(rd());
       std::shuffle(Paquet.begin(), Paquet.end(), re);
    }
    Les résultats sont différents à chaque fois, sans avoir besoin de faire un srand(time(NULL)) au début du programme. Après ça vaut peut être le coup de n'instancier le random_device et le default_random_engine qu'une seule fois plutôt qu'à chaque appel de Mélanger(), je ne sais pas.

  6. #6
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Oui, herb sutter par exemple conseille régulièrement de l'utilser dès que possible, perso ça me hérisse le poil et je ne vois pas ce qu'on gagne réellement à écrire for(auto& c : Paquet) au lieu de for(Carte& c : Paquet) (sauf que c'est plus clair qu'on manipule des Carte dans le deuxième cas)

    Non, ça c'est faux les performances sont exactement identique dans ce cas.
    Je pense que tu confonds avec qqchose du style :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    auto lambda = [] (){do_something();};
    std::function<void()> f = [] (){do_something();};
    Dans ce cas, en effet auto est plus performant vu qu'il permet de conserver le vrai type de la lambda, au lieu de devoir copier la lambda dans une std::fonction (donc avec potentiellement un new et un appel à une fonction virtuelle)



    Si tu es en C++11, il y a le nouveau header <random> qui permet de faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void Melanger()
    {
       std::random_device rd;
       std::default_random_engine re(rd());
       std::shuffle(Paquet.begin(), Paquet.end(), re);
    }
    Les résultats sont différents à chaque fois, sans avoir besoin de faire un srand(time(NULL)) au début du programme. Après ça vaut peut être le coup de n'instancier le random_device et le default_random_engine qu'une seule fois plutôt qu'à chaque appel de Mélanger(), je ne sais pas.

    ok merci encore pour ton aide, je vais regarder tout ça



    Edit : j'ai une exception qui est levée avec le bout de code (que j'ai bêtement copié-collé)de Mélanger comme ça ; ça fonctionne pas juste comme ça ?

    terminate called after throwing an instance of 'std::runtime_error'
    what(): random_device::random_device(const std::string&)

    This application has requested the Runtime to terminate it in an unusual way.
    Please contact the application's support team for more information.
    Nullius in verba

  7. #7
    Membre éprouvé

    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 533
    Points : 1 086
    Points
    1 086
    Par défaut
    Note qu'il est quand même possible de faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for (auto& c : Paquet) {...}
    //ou même
    for (const auto& c : Paquet) {...}

  8. #8
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par cob59 Voir le message
    Note qu'il est quand même possible de faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for (auto& c : Paquet) {...}
    //ou même
    for (const auto& c : Paquet) {...}
    ouep ça je connaissais (je l'ai vu dans le topic sur gcc dans les explications sur le range-based for)

    J'ai edit mon message car ce n'est pas totalement réglé malheureusement ^^
    Nullius in verba

  9. #9
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Edit : j'ai une exception qui est levée avec le bout de code (que j'ai bêtement copié-collé)de Mélanger comme ça ; ça fonctionne pas juste comme ça ?
    Normalement oui, en tout cas sur Visual Studio 2012, ça fonctionne.
    Mais ça demande un compilateur avec une biblio standard conforme au standard C++11.

    En copiant collant le message d'erreur, je tombe sur ce sujet (http://www.gamedev.net/topic/622301-mingw-g-462-crash/) qui laisse entendre que ce bug vient de mingw.
    J'ai l'impression que le plus sage reste d'utiliser ce bon vieux srand, au moins encore quelques années le temps que tous les compilos soient au point...

  10. #10
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Normalement oui, en tout cas sur Visual Studio 2012, ça fonctionne.
    Mais ça demande un compilateur avec une biblio standard conforme au standard C++11.

    En copiant collant le message d'erreur, je tombe sur ce sujet (http://www.gamedev.net/topic/622301-mingw-g-462-crash/) qui laisse entendre que ce bug vient de mingw.
    J'ai l'impression que le plus sage reste d'utiliser ce bon vieux srand, au moins encore quelques années le temps que tous les compilos soient au point...
    Ok, merci pour l'info
    Nullius in verba

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

Discussions similaires

  1. Doublon après Créer un Element dans un workflow
    Par Gratiano dans le forum SharePoint
    Réponses: 3
    Dernier message: 02/10/2014, 10h01
  2. [langage] Supprimer un élément dans une liste
    Par myjuna dans le forum Langage
    Réponses: 15
    Dernier message: 06/08/2014, 11h49
  3. [MySQL] erreur SQL, refuse de m'inserer element dans table nouvellement créée
    Par gael-abdelhadi dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 31/03/2011, 13h10
  4. Probleme ajouter element dans classe vector
    Par salmgh dans le forum Servlets/JSP
    Réponses: 5
    Dernier message: 09/12/2008, 13h53
  5. Réponses: 12
    Dernier message: 26/02/2003, 08h14

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