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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Algorithme itératif pour générer les arrangements de p éléments parmi n
Un billet blog de Denis Hulo

Le , par User

0PARTAGES

I. Introduction

Après les combinaisons, on s'intéresse cette fois à un algorithme itératif pour générer tous les arrangements de p éléments parmi n.

Il va nous permettre ensuite d'implémenter une fonction en Python qui pourra générer un grand nombre d'arrangements sans avoir besoin de les stocker en mémoire.

L'objectif est toujours d'expliquer le fonctionnement de ce type d'algorithme sans trop s'attarder sur l'écriture de la fonction en Python.


II. Règles pour représenter et générer ces arrangements

Si on souhaite par exemple obtenir toutes les façons de tirer successivement 3 boules parmi 4 en tenant compte de l'ordre du tirage, il nous faut tout d'abord numéroter dans l'ordre les 4 boules de la manière suivante :

(0,1,2,3)

On fait commencer les indices des boules à 0 comme pour les indices des tuples en Python.

Et si maintenant on tire successivement les 3 premières boules dans l'ordre, on obtient un arrangement sous forme de tuple, contenant leurs indices respectifs :

(0,1,2)

Mais comme l'ordre du tirage est maintenant pris en compte, choisir (0,1,2) est différent de choisir (0,2,1) ou (1,0,2) ou (1,2,0) ou (2,0,1) ou (2,1,0).

On représentera alors le tirage de 3 boules quelconques par le tuple :

(i0,i1,i2) avec i0≠i1, i0≠i2 et i1≠i2

On encore, si on souhaite indiquer qu'il s'agit de l'arrangement de rang j :

(ij,0,ij,1,ij,2) avec ij,0≠ij,1, ij,0≠ij,2 et ij,1≠ij,2.


Tableau des arrangements et règles de passage entre 2 séquences d'indices successives :

603518

ij,k désignant l'indice k de l'arrangement j.

Dans le cas général, l'arrangement de la ligne j de p boules parmi n peut donc s'écrire sous forme de tuple :

(ij,0, ij,1,.., ij,k-1, ij,k, ij,k+1,.., ij,p-1) chaque indice étant unique (pas de doublon).

On aura alors des tirages qui s'étalent de (0,1,..,k,..,p-1) à ((n-1),(n-2),..,(n-k-1),..,n-p).

On cherchera donc le 1er indice ij,k en partant de la droite qui puisse être incrémenté tout en restant différent des indices qui le précèdent (ij,0, ij,1,.., ij,k-1).

On obtient ensuite l'arrangement de la ligne j+1 représenté sous forme de tuple :

(ij,0, ij,1,.., ij,k-1, ij+1,k, ij+1,k+1,.., ij+1,p-1) chaque indice étant unique (pas de doublon).

Les k premiers indices ne changent pas par rapport au tuple précédent.

On obtient ij+1,k après avoir incrémenté ij,k.
ij+1,k étant le plus petit indice supérieur à ij,k et différent des indices qui le précèdent

Sans oublier bien sûr de réinitialiser aussi les indices suivants pour un prochain cycle :


  • ij+1,k+1 est le plus petit indice supérieur ou égal à 0 et différent des indices qui le précèdent.
  • ij+1,k+2 est le plus petit indice supérieur à ij+1,k+1 et différent des indices qui le précèdent.
  • ij+1,k+3 est le plus petit indice supérieur à ij+1,k+2 et différent des indices qui le précèdent.
  • etc..


    Toutes les séquences d'indices qui comportent des doublons seront donc ignorées au cours de la procédure.


    III. Fonctions itératives en Python

    Nous utilisons l'instruction yield qui permet de parcourir l'ensemble des arrangements, sans avoir besoin de les stocker en mémoire :

    def arrangements_v1(p,n):

    if p>n:
    return

    indices=list(range(p)) # on initialise la liste des indices [0,1,2,..,p-1]
    yield tuple(indices) # ordre pour générer le tuple

    i=p-1 # on part de la fin de la liste des indices
    k=p # variable utilisé pour incrémenter ou initialiser les indices

    while i!=-1: # on parcourt les indices de la droite vers la gauche : p-1 -> 0
    if k < n: # si la valeur d'indice maxi n'a pas été dépassée
    if k not in indices[:i]: # si l'indice k n'est pas présent parmi les indices précédant indices
    indices=k # on incrémente indices avec la valeur de k
    k=0;j=i+1 # on met k à 0 pour ensuite initialiser les prochains indices : nouveau cycle (ex. : 0,1,2,..).
    while (j < p): # on parcourt les prochains indices
    if k not in indices[:i+1]: # si l'indice k n'est pas présent parmi les indices précédant indices[i+1]
    indices=k # on initialise indices avec la valeur de k
    j+=1 # prochain indice
    k+=1
    yield tuple(indices) # ordre pour générer le tuple
    i=p-1 # on revient sur le dernier indice à incrémenter
    k=indices
    k+=1 # on incrémente k
    else: # sinon
    i-=1 # on remonte les indices vers la gauche
    k=indices+1 # on met à jour k pour le prochain indice

    return

    Exemple de code permettant de générer les arrangements de 9 éléments parmi 14 :

    iterator = arrangements_v1(9,14)

    # on génère les arrangements dans une boucle
    for a in iterator:
    # faire quelque chose avec l'arrangement a

    Une version pour générer des arrangements de caractères alphanumériques :

    def arrangements_v2(p,e):

    n=len(e) # longueur de la liste de caractères

    if p>n:
    return

    indices=list(range(p)) # on initialise la liste des indices [0,1,2,..,p-1]
    yield tuple(e for i in indices) # ordre pour générer le tuple

    i=p-1 # on part de la fin de la liste des indices
    k=p # variable utilisé pour incrémenter ou initialiser les indices

    while i!=-1: # on parcourt les indices de la droite vers la gauche : p-1 -> 0
    if k < n: # si la valeur d'indice maxi n'a pas été dépassée
    if k not in indices[:i]: # si l'indice k n'est pas présent parmi les indices précédant indices
    indices=k # on incrémente indices avec la valeur de k
    k=0;j=i+1 # on met k à 0 pour ensuite initialiser les prochains indices : nouveau cycle (ex. : 0,1,2,..).
    while (j < p): # on parcourt les prochains indices
    if k not in indices[:i+1]: # si l'indice k n'est pas présent parmi les indices précédant indices
    indices=k # on initialise indices avec la valeur de k
    j+=1 # prochain indice
    k+=1
    yield tuple(e for i in indices) # ordre pour générer le tuple
    i=p-1 # on revient sur le dernier indice à incrémenter
    k=indices
    k+=1 # on incrémente k
    else: # sinon
    i-=1 # on remonte les indices vers la gauche
    k=indices+1 # on met à jour k pour le prochain indice

    return

    Vous trouverez ce type de fonction dans la librairie itertools pour Python.

    IV. Conclusion

    Après avoir numéroté les n éléments d'un ensemble donné de 0 à n-1, nous avons pu représenter chacun des arrangements de p éléments pris dans cet ensemble à l'aide de séquences d'indices. Ceci nous a ensuite permis d'identifier certaines règles sur ces séquences. Finalement nous avons pu écrire la fonction permettant de générer dans l'ordre toutes les arrangements de p éléments parmi n.
  • Vous avez lu gratuitement 1 586 articles depuis plus d'un an.
    Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

    Une erreur dans cette actualité ? Signalez-nous-la !