Idée pour simuler un tirage sans ordre et sans remise

Utilisation de Xcas

Modérateur : xcasadmin

loic
Messages : 168
Inscription : ven. mars 14, 2008 7:20 pm

Idée pour simuler un tirage sans ordre et sans remise

Message par loic » sam. mars 07, 2009 5:09 pm

Bonjour,

Afin de m'entraîner un peu à programmer avec XCas,
j'ai voulu essayé de simuler l'exercice de dénombrement suivant:
Un jury est composé de 10 membres tirés au sort parmi 8 hommes et 9 femmes.
- Combien de jurys différents peut-on former?
- Combien de jurys avec 5 femmes et 5 hommes peut-on former?
- Monsieur X refuse de siéger avec Madame Y. Combien de jurys peut-on former dans ces conditions?
Pour modéliser cette situation, je souhaitais donc créer une liste de tous les jurys possibles c'est à dire de 10 personnes prises sans ordre et sans remise parmi 17.

Je dois avouer que le seul procédé que j'ai trouvé est le suivant:
L'ensemble de départ est composé de 17 personnes, il est donc formé de 2^17 parties.
Chaque partie peut être représentée par un nombre en binaire compris entre 0 et 2^17-1
un "1" en ième position indique qu'on prend la personne n°i
un "0" en ième position indique qu'on ne prend pas la personne n°i

Je fais donc varier un entier entre 0 et 2^17-1.
Pour chaque décomposition en binaire, je garde l'élément si la somme des chiffres vaut 10.

Ma question est juste de savoir s'il y a plus simple où s'il existe une fonction intégrée permettant de réaliser cette simulation.
Je vous joins quand même mon programme: (Temps d'exécution: environ 2 min )

Code : Tout sélectionner


denJurys():=
  {
    local jurys,binaire,j;
   // Nombre de Jurys avec parités.
    local parite:=0;
   // Monsieur X:"m1"
   // Madame Y: "f1"
   // Nombre de Jurys où M. X ne siège pas avec Mme Y
    local jurysXY:=0;
    // Liste des membres (8 hommes, 9 femmes)
    local membres:=["m1","m2","m3","m4","m5","m6","m7","m8","f1","f2","f3","f4","f5","f6","f7","f8","f9"];
    jurys:=[];
    // Création de l'ensemble des jurys possibles.
    for (j:=0;j<2^17;j++){
      binaire:=enBinaire(j);
      if(sum(binaire)==10){
        local k,tmpList:=[];
        for(k:=0;k<size(binaire);k++){
          if (binaire[k]==1) tmpList:=append(tmpList,membres[k+17-size(binaire)]);
        }
        jurys:=append(jurys,tmpList);
      }
    }
    print("Nombre de Jurys possibles:",size(jurys));

    // Parcours de l'ensemble des jurys pour trouver:
    // - le nombre de jurys avec parité hommes-femmes
    // - le nombre de jurys où M. X ne siègent pas avec Mme Y

    for(j:=0;j<size(jurys);j++){
      local k,c:=0;
      local bX:=false;
      local bY:=false;
      for(k:=0;k<size(jurys[j]);k++){
        local mot:=jurys[j][k];
        local initiale;        
        initiale:=mot[0];
          if (initiale=="m") c++;
          if (mot=="m1") bX:=true;
          if (mot=="f1") bY:=true;
      }
      if (c==5) parite++;
      if (bX==false or bY==false) jurysXY++;
    }
    print("Nombre de Jurys avec parité hommes-femmes:",parite);
    print("Nombre de Jurys où M. X ne siège pas avec Mme Y:",jurysXY);

}:;

// Renvoie une liste représentant l'écriture en binaire du nombre n
// Exemple enBinaire(12) retourne [1,1,0,0]
enBinaire(n):=
  {
    if (n<2) return [n];
    else {
      local divEucl;
      divEucl:=iquorem(n,2);
      return concat(enBinaire(divEucl[0]),divEucl[1]);
    }
 }:;


parisse
Messages : 5896
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Message par parisse » dim. mars 08, 2009 7:58 am

Le schéma général du programme me parait excellent. Voici quelques astuces qui permettent d'améliorer un peu la vitesse d'exécution:
1/ au lieu de EnBinaire, on peut utiliser convert(.,base,2)
2/ preallouer les listes jury et tmpList et utiliser l'opérateur d'affectation en place =< au lieu de := afin d'éviter de réallouer et copier la liste à chaque itération. Mais attention à utiliser avec précaution, par exemple le tmpList:=[0$10] doit etre réexécuté à chaque fois sinon c'est le même élément qui sera copié 19448 fois dans jury.
3/ j'ai supprimé les déclarations de variables locales internes pour les mettre au début (chaque déclaration de variable locale est interprétée et coute donc un peu en temps d'exécution).
J'obtiens l'exécution en 24s sur mon PC (je n'ai pas eu la patience d'attendre le prog. original). On doit pouvoir encore améliorer la vitesse au détriment de la clarté en n'utilisant pas "m" et "f" mais seulement les indices et endistinguant hommes/femmes par un indice limite.

Code : Tout sélectionner

denJurys():=
  {
    local jury,binaire,j,n,m,K,k,c,bX,bY,mot,initiale,tmpList;
   // Nombre de Jurys avec parités.
    local parite:=0;
   // Monsieur X:"m1"
   // Madame Y: "f1"
   // Nombre de Jurys où M. X ne siège pas avec Mme Y
    local jurysXY:=0;
    // Liste des membres (8 hommes, 9 femmes)
    local membres:=["m1","m2","m3","m4","m5","m6","m7","m8","f1","f2","f3","f4","f5","f6","f7","f8","f9"];
    jurys:=[0$comb(17,10)];
    // Création de l'ensemble des jurys possibles.
    for (j:=1;j<2^17;j++){
      binaire:=convert(j,base,2);
      if(sum(binaire)==10){
        m:=0;
        tmpList:=[0$10];
        for(K:=0;K<size(binaire);K++){
          if (binaire[K]==1){ 
            tmpList[m]=<membres[K];
            m++;
          }
        }
        jurys[n] =< tmpList ;
        n++;
      }
    }
    print("Nombre de Jurys possibles:",size(jurys));

    // Parcours de l'ensemble des jurys pour trouver:
    // - le nombre de jurys avec parité hommes-femmes
    // - le nombre de jurys où M. X ne siègent pas avec Mme Y

    for(j:=0;j<size(jurys);j++){
      c:=0;
      bX:=faux;
      bY:=faux;
      binaire:=jurys[j];
      for(k:=0;k<size(binaire);k++){
        mot:=binaire[k];
        initiale:=mot[0];
          if (initiale=="m") c++;
          if (mot=="m1") bX:=vrai;
          if (mot=="f1") bY:=vrai;
      }
      if (c==5) parite++;
      if (bX==faux or bY==faux) jurysXY++;
    }
    print("Nombre de Jurys avec parité hommes-femmes:",parite);
    print("Nombre de Jurys où M. X ne siège pas avec Mme Y:",jurysXY);

}:;

loic
Messages : 168
Inscription : ven. mars 14, 2008 7:20 pm

Message par loic » dim. mars 08, 2009 11:22 am

Merci pour ces clarifications.
utiliser l'opérateur d'affectation en place =< au lieu de := afin d'éviter de réallouer et copier la liste à chaque itération.
Je cherchais une telle fonctionnalité mais je ne l'avais pas trouvé dans la doc. Comme quoi, on loupe toujours certaines choses!
On doit pouvoir encore améliorer la vitesse au détriment de la clarté en n'utilisant pas "m" et "f" mais seulement les indices et endistinguant hommes/femmes par un indice limite.
Oui, mais je ne voulais pas poster un programme illisible :)

Je vous livre l'état des performances au fil des améliorations:

- Programme original..............................: Evaluation time: 231 s
- En supprimant la procédure enBinaire............: Evaluation time: 75.27 s (aïe, aïe, aïe... une si belle récursivité!)
- En utilisant l'affectation dynamique............: Evaluation time: 10.12 s (pas mal!)
- En n'utilisant plus la liste ["m1", ...., "f9"].: Evaluation time: 8.99 s


Quelques questions encore toutefois:
1) Pourquoi dans la version originale, ai-je le warning suivant concernant la fonction enBinaire?
// Parsing denJurys
// Warning: enBinaire declared as global variable(s) compiling denJurys
// Parsing enBinaire
// Warning: enBinaire declared as global variable(s) compiling enBinaire
enBinaire: recursive definition
2) Je vois que dans votre programme, vous utilisez les booléens vrai ou faux et non pas true et false
Lorsque l'interpréteur parse le fichier, il me les signale comme variable globale, c'est normal?

3) J'ai encore un doute sur l'utilisation de l'opérateur =<.
Je ne comprends pas pourquoi tmpList doit être réinitialisée à chaque itération, avec

Code : Tout sélectionner

 
tmpList:=[0$10]; 
et ne peut pas être simplement initialisée tout au début de la boucle.

loic
Messages : 168
Inscription : ven. mars 14, 2008 7:20 pm

Message par loic » dim. mars 08, 2009 11:28 am

Ah!, petit oubli

Pourquoi convert(0,base,2) ne renvoie pas [0]?

Merci d'avance,

Loïc

parisse
Messages : 5896
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Message par parisse » dim. mars 08, 2009 12:29 pm

qques élements de réponse:
1/ sur EnBinaire: c'est sur qu'une récursion est plus couteuse qu'un programme itératif (mais parfois moins lisible). Mais je pense que la difference de temps d'execution vient surtout de interprété/compilé, convert étant écrit en C (++). Le warning à la compil de EnBinaire c'est pour que l'utilisateur soit bien conscient qu'il effectue un appel récursif.
2/ c'est vrai que =< vs := n'est pas très commenté. C'est une addition assez récente en fait... =< affecte par référence et non par valeur, donc on modifie toutes les copies existantes de la liste. C'est pour ça qu'il faut refaire une initialisation de tmpList, sinon quand on le met dans jury[], jury[0], jury[1], etc. désignent la même référence d'objet et donc tmpList[]:= va modifier jury[0][], jury[1][], etc. C'est aussi pour ça qu'on n'insiste pas trop sur =<, car son usage peut entrainer des bugs difficiles à comprendre.
3/ sur vrai/faux et true/false, c'est sans doute un problème de localisation. Xcas utilise la langue pour traduire des mots-clefs (fichier doc/fr/keywords), sous linux on voit ca au lancement depuis une console
// Using keyword file /usr/local/share/giac/doc/fr/keywords
Le problème se pose uniquement pour des sources, pas pour les fichiers sessions .xws qui sont sauvegardés avec les mots-clefs en anglais. Les mots clefs en anglais passent également toujours même si on est en français.
4/ pour convert(0,base,2), j'ai adopté la convention que ca devait contenir un 1 significatif. Et j'ai suivi la convention de maple pour l'ordre qui est inverse de l'ordre habituel. On peut utiliser revlist pour revenir à l'ordre habituel. Par contre il y a un bug dans sum qui devrait renvoyer 0 avec une liste vide comme argument (d'ou la boucle commencee à 1, en fait on pourrait la commencer à 2^10-1)
Question subsidiaire: etes-vous l'auteur de xlogo?

loic
Messages : 168
Inscription : ven. mars 14, 2008 7:20 pm

Message par loic » dim. mars 08, 2009 1:00 pm

C'est pour ça qu'il faut refaire une initialisation de tmpList, sinon quand on le met dans jury[], jury[0], jury[1], etc. désignent la même référence d'objet et donc tmpList[]:= va modifier jury[0][], jury[1][], etc. C'est aussi pour ça qu'on n'insiste pas trop sur =<, car son usage peut entrainer des bugs difficiles à comprendre.
Mais bon sang, c'est bien sûr! Je viens enfin de comprendre. Le problème est sur la référence dans jury[] qui pointe vers le même objet! Je n'avais pas vu cela ... Merci!
Question subsidiaire: etes-vous l'auteur de xlogo?
Oui, en effet, mais je dois avouer que je n'ai découvert que très récemment que Xcas manipulait aussi la tortue. Je ne l'ai pas essayé pour l'instant. Vous utilisez XLogo de temps à temps?

Ce n'est pas pour faire fonctionner la brosse à reluire .... mais j'ai fait pas mal de test sur divers secteurs entre Maple, XCas et Sage et je dois avouer que XCas me parait réellement supérieur aux deux autres pour ce qui est de la partie calcul formel. Globalement, il simplifie mieux (tout seul en utilisant juste simplify()), des meilleurs résultats sur l'intégration, les calculs de limites et autres...
Côté programmation, le langage utilisé dans XCas est tout à fait sympatique. J'ai une petite préférence au langage typé Python utilisé dans Sage mais bon... Vu les possibilités très vastes de programmation ce n'est pas un problème!
Le seul endroit où j'ai trouvé Sage légèrement supérieur à Xcas est pour le tracé des fonctions 2D et 3D avec un meilleur rendu à mon sens.
Ce ne sont là que quelques commentaires . Plus j'explore XCas, plus je suis impressionné par l'étendue de ces possibilités et par la qualité du logiciel! (De plus, à peine un bug est signalé qu'il est déjà corrigé!)
Mille bravos! :D

parisse
Messages : 5896
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Message par parisse » dim. mars 08, 2009 5:49 pm

loic a écrit : Oui, en effet, mais je dois avouer que je n'ai découvert que très récemment que Xcas manipulait aussi la tortue. Je ne l'ai pas essayé pour l'instant. Vous utilisez XLogo de temps à temps?
J'avoue que je n'ai pas encore essayé. Mais en téléchargeant le manuel, xlogo semble très intéressant, avec en plus des commandes habituelles, la tortue dans l'espace.
J'ai rajouté un petit module logo à xcas, à la demande de Renée De Graeve (qui écrit la doc de Xcas), elle avait beaucoup travaillé avec ce langage il y a quelques années dans des classes primaires. Mais l'interface de xcas n'a pas du tout été testée en primaire et nécessiterait sans doute des modifs importantes pour être utilisable à ce niveau. J'imagine par contre que xlogo est beaucoup plus mur à ce niveau.
Ce n'est pas pour faire fonctionner la brosse à reluire .... mais j'ai fait pas mal de test sur divers secteurs entre Maple, XCas et Sage et je dois avouer que XCas me parait réellement supérieur aux deux autres pour ce qui est de la partie calcul formel. Globalement, il simplifie mieux (tout seul en utilisant juste simplify()), des meilleurs résultats sur l'intégration, les calculs de limites et autres...
Côté programmation, le langage utilisé dans XCas est tout à fait sympatique. J'ai une petite préférence au langage typé Python utilisé dans Sage mais bon... Vu les possibilités très vastes de programmation ce n'est pas un problème!
Le seul endroit où j'ai trouvé Sage légèrement supérieur à Xcas est pour le tracé des fonctions 2D et 3D avec un meilleur rendu à mon sens.
Merci! Le choix du langage de xcas a surtout été fait pour avoir une bonne compatibilité avec les logiciels de calcul formel populaires en France, maple et les calcs TI.
Concernant les tracés 2-d et 3-d, il y aurait certainement des améliorations à apporter, mais comme je suis malheureusement essentiellement seul à développer, je n'en ai pas le temps. Si par hasard vous étiez intéressé à développer pour le projet xcas, ou à utiliser les fonctionnalités de la librairie giac pour enrichir les fonctionnalités de xlogo, je serais très intéressé!

Répondre