4.5 Exemples d’utilisation des tableaux
4.5.4 Jouons `a l’escarmouche
4.5.3 Le crible d’Eratosthene
On cherche ici `a trouver tous les nombres premiers de l’intervalle [1, N]. La solution d´ej`a connue des grecs consiste `a ´ecrire tous les nombres de l’intervalle les uns `a la suite des autres. Le plus petit nombre premier est 2. On raye alors tous les multiples de 2 plus grands que 2 de l’intervalle, ils ne risquent pas d’ˆetre premiers. Le premier nombre qui n’a pas ´et´e ray´e au-del`a du nombre premier courant est lui-mˆeme premier, c’est le suivant `a traiter. On raye ainsi les multiples de 3 sauf 3, etc. On s’arrˆete quand on s’apprˆete `a ´eliminer les multiples de p >√
N (rappelons que tout nombre non premier plus petit que N a un diviseur premier 6√
N).
Comment mod´eliser le crible ? On utilise un tableau de bool´eensestpremier[N+1]
qui repr´esentera l’intervalle [1, N]. Il est initialis´e `atrue au d´epart, car aucun nombre n’est ray´e. `A la fin du calcul,p≥2 est premier ssiestpremier[p] == true. On trouve le programme complet dans la figure 4.1.
Remarquons que la ligne kp = 2*p;
peut ˆetre avantageusement remplac´ee par kp = p*p;
car tous les multiples de p de la forme upavec u < p ont d´ej`a ´et´e ray´es du tableau `a une ´etape pr´ec´edente.
Il existe de nombreuses astuces permettant d’acc´el´erer le crible. Notons ´egalement que l’on peut se servir du tableau des nombres premiers pour trouver les petits facteurs de petits entiers. Ce n’est pas la meilleure m´ethode connue pour trouver des nombres premiers ou factoriser les nombres qui ne le sont pas. Revenez donc me voir en Majeure 2 si ¸ca vous int´eresse.
4.5.4 Jouons `a l’escarmouche
On peut ´egalement se servir de tableaux pour repr´esenter des objets a priori plus compliqu´es. Nous allons d´ecrire ici une variante simplifi´ee du c´el`ebre jeu de bataille, que nous appelerons escarmouche. La r`egle est simple : le donneur distribue 32 cartes (num´erot´ees de 1 `a 32) `a deux joueurs, sous la forme de deux piles de cartes, face sur le dessous. `A chaque tour, les deux joueurs, appel´es Alice et Bob, retournent la carte du dessus de leur pile. Si la carte d’Alice est plus forte que celle de Bob, elle marque 1
// Retourne le tableau des nombres premiers de l’intervalle [2..N]
static int[] Eratosthene(int N){
boolean[] estpremier = new boolean[N+1];
int p, kp, nbp;
// initialisation
for(int n = 2; n < N+1; n++) estpremier[n] = true;
// boucle d’´elimination p = 2;
while(p*p <= N){
// ´elimination des multiples de p
// on a d´ej`a ´elimin´e les multiples de q < p kp = 2*p; // ( cf. remarque)
while(kp <= N){
estpremier[kp] = false;
kp += p;
}
// recherche du nombre premier suivant do{
p++;
} while(!estpremier[p]);
}
// comptons tous les nombres premiers <= N nbp = 0;
for(int n = 2; n <= N; n++) if(estpremier[n])
nbp++;
// mettons les nombres premiers dans un tableau int[] tp = new int[nbp];
for(int n = 2, i = 0; n <= N; n++) if(estpremier[n])
tp[i++] = n;
return tp;
}
Fig. 4.1 – Crible d’Eratosthene.
point ; si sa carte est plus faible, c’est Bob qui marque 1 point. Gagne celui des deux joueurs qui a marqu´e le plus de points `a la fin des piles.
Le programme de jeu doit contenir deux phases : dans la premi`ere, le programme bat et distribue les cartes entre les deux joueurs. Dans un second temps, le jeu se d´eroule.
Nous allons stocker les cartes dans un tableau donne[0..32[ avec la convention que la carte du dessus se trouve en position 31.
Pour la premi`ere phase, battre le jeu revient `a fabriquer une permutation au ha-sard des ´el´ements du tableau donne. L’algorithme le plus efficace pour cela utilise un g´en´erateur al´eatoire (la fonction Math.random()de Java, qui renvoie un r´eel al´eatoire entre 0 et 1), et fonctionne suivant le principe suivant. On commence par tirer un indice iau hasard entre 0 et 31 et on permutedonne[i]et donne[31]. On continue alors avec le reste du tableau, en tirant un indice entre 0 et 30, etc. La fonction Java est alors (nous allons ici syst´ematiquement utiliser le passage par r´ef´erence des tableaux) :
static void battre(int[] donne){ int n = donne.length, i, j, tmp;
for(i = n-1; i > 0; i--){
// on choisit un entier j de [0..i]
j = (int) (Math.random() * (i+1));
// on permute donne[i] et donne[j]
tmp = donne[i]; donne[i] = donne[j]; donne[j] = tmp;
} }
La fonction qui cr´ee une donne `a partir d’un paquet de ncartes est alors : static int[] jeu(int n){
int[] jeu = new int[n];
for(int i = 0; i < n; i++) jeu[i] = i+1;
battre(jeu);
return jeu;
}
et nous donnons maintenant le programme principal : public static void main(String[] args){
int[] donne;
donne = jeu(32);
jouer(donne);
}
Nous allons maintenant jouer. Cela se passe en deux temps : dans le premier, le donneur distribue les cartes entre les deux joueurs, Alice et Bob. Dans le second, les deux joueurs jouent :
static void jouer(int[] donne){
int[] jeuA = new int[donne.length/2];
int[] jeuB = new int[donne.length/2];
distribuer(jeuA, jeuB, donne);
jouerAB(jeuA, jeuB);
}
Le tableau donne[0..31]est distribu´ee en deux tas, en commen¸cant par Alice, qui va recevoir les cartes de rang pair, et Bob celles de rang impair. Les cartes sont donn´ees
`a partir de l’indice 31 :
// donne[] contient les cartes qu’on distribue `a partir de la fin // on remplit jeuA et jeuB `a partir de 0
static void distribuer(int[] jeuA, int[] jeuB, int[] donne){ int iA = 0, iB = 0;
for(int i = donne.length-1; i >= 0; i--){ if((i % 2) == 0)
jeuA[iA++] = donne[i];
else
jeuB[iB++] = donne[i];
} }
Il ne reste plus qu’`a jouer et `a afficher le gagnant. On introduit les deux variables gainA et gainB qui contiennent le nombre de fois o`u la carte de rang id’Alice (resp.
Bob) est de valeur plus forte que celle de Bob (resp. Alice) : static void jouerAB(int[] jeuA, int[] jeuB){
int gainA = 0, gainB = 0;
for(int i = jeuA.length-1; i >= 0; i--){ if(jeuA[i] > jeuB[i])
gainA++;
else
gainB++;
}
if(gainA > gainB) System.out.println("A gagne");
else System.out.println("B gagne");
}
Exercice.(Programmation du jeu de bataille) Dans le jeu de bataille (toujours avec les cartes 1..32), le joueur qui remporte un pli le stocke dans une deuxi`eme pile `a cˆot´e de sa pile courante, les cartes ´etant stock´ees dans l’ordre d’arriv´ee, formant une nouvelle pile. Quand il a fini sa premi`ere pile, il la remplace par la seconde et continue `a jouer.
Le jeu s’arrˆete quand un des deux joueurs n’a plus de cartes. Programmer ce jeu.