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

Python Discussion :

comment éviter plein de boucles imbriquées.


Sujet :

Python

  1. #21
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par josmiley Voir le message
    Il faut autant de lambda que de niveau.
    Effectivement, en faisant une analogie à 4 niveaux j'ai écrit ceci...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    l1 = lambda *i:[l2(*i+(j,))    for j in range(1,5)]
    l2 = lambda *i:[l3(*i+(j,))    for j in range(1,7)]
    l3 = lambda *i:[l4(*i+(j,))    for j in range(1,10) if i[0]!=10]
    l4 = lambda *i:[print(*i+(j,)) for j in range(1, 3) if i[1]//i[0]]
     
    l1()
    ... et ça fonctionne. Mais déjà je ne pige pas le if i[0]!=10. Tu avais mis range(1, 11) et le test était sur 11, comme je l'ai dit en procédant par analogie sur [c]range(1, 10) j'ai écrit if i[0]!=10 mais j'ai ensuite remplacé par if true et ça a donné le même résultat y compris dans ton code d'origine. De même le if i[1] // i[0] lui aussi me laisse perplexe...

    Citation Envoyé par josmiley Voir le message
    L'avantage est que c'est facile à lire
    Euh...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #22
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 049
    Points : 1 380
    Points
    1 380
    Par défaut
    J'avais mis des filtres bidons, c'était juste pour l'exemple, mais i[0] est émis par range(1,21) du niveau l1.

  3. #23
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par josmiley Voir le message
    J'avais mis des filtres bidons, c'était juste pour l'exemple
    Ok, j'avais pas pigé, je pensais que ç'était pour aider dans l'itération. Pffouh pas le cerveau clair ce matin...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #24
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Bonjour,

    Pour imbriquer des boucles, la solution est d'utiliser une generator expression. Voici un exemple dans lequel on itère sur les triplets de chiffres et où on écrit une ligne si la somme des deux premiers chiffres est un multiple de 5 et si la somme des trois chiffres est un multiple de 11 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for a, b, c in (
        (a, b, c)
        for a in range(10)
        for b in range(10)
        if (a + b) % 5 == 0
        for c in range(10)
        if (a + b + c) % 11 == 0
    ):
        print(f"{(a, b, c)}: {a}+{b}=={a+b} and {a}+{b}+{c}=={a+b+c}")
    Résultat :
    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
    (0, 0, 0): 0+0==0 and 0+0+0==0
    (0, 5, 6): 0+5==5 and 0+5+6==11
    (1, 4, 6): 1+4==5 and 1+4+6==11
    (1, 9, 1): 1+9==10 and 1+9+1==11
    (2, 3, 6): 2+3==5 and 2+3+6==11
    (2, 8, 1): 2+8==10 and 2+8+1==11
    (3, 2, 6): 3+2==5 and 3+2+6==11
    (3, 7, 1): 3+7==10 and 3+7+1==11
    (4, 1, 6): 4+1==5 and 4+1+6==11
    (4, 6, 1): 4+6==10 and 4+6+1==11
    (5, 0, 6): 5+0==5 and 5+0+6==11
    (5, 5, 1): 5+5==10 and 5+5+1==11
    (6, 4, 1): 6+4==10 and 6+4+1==11
    (6, 9, 7): 6+9==15 and 6+9+7==22
    (7, 3, 1): 7+3==10 and 7+3+1==11
    (7, 8, 7): 7+8==15 and 7+8+7==22
    (8, 2, 1): 8+2==10 and 8+2+1==11
    (8, 7, 7): 8+7==15 and 8+7+7==22
    (9, 1, 1): 9+1==10 and 9+1+1==11
    (9, 6, 7): 9+6==15 and 9+6+7==22
    Ce code équivaut à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def my_generator():
        for a in range(10):
            for b in range(10):
                if (a + b) % 5 == 0:
                    for c in range(10):
                        if (a + b + c) % 11 == 0:
                            yield (a, b, c)
     
    for a, b, c in my_generator():
        print(f"{(a, b, c)}: {a}+{b}=={a+b} and {a}+{b}+{c}=={a+b+c}")
    Remarque : dans mon exemple, comme il n'y a que 20 triplets qui correspondent au critère de filtre, ce ne serait pas gênant de générer d'abord la liste complète de ces 20 triplets, puis d'itérer sur cette liste. Donc on pourrait très bien remplacer la generator expression par une liste en compréhension :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for a, b, c in [
        (a, b, c)
        for a in range(10)
        for b in range(10)
        if (a + b) % 5 == 0
        for c in range(10)
        if (a + b + c) % 11 == 0
    ]:
        print(f"{(a, b, c)}: {a}+{b}=={a+b} and {a}+{b}+{c}=={a+b+c}")
    Par contre, si la liste avait été très grande, alors l'avantage de passer par une generator expression est qu'on peut directement itérer dessus sans avoir besoin de garder en mémoire les triplets précédents qui répondaient aux critères de filtre.

    En particulier, d'après le premier message du topic, tbc92 a 11 boucles imbriquées et recherche le minimum d'une certaine fonction :
    Citation Envoyé par tbc92 Voir le message
    Je veux faire un truc comme ça :
    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
     
    for i1 = 1 to  n1 step p1
      for i2 = 1 to n2 step  p2
          for i3 = 1 to n3 step p3
              if ok00 (i1,i2,i3)  then 
                 for i4 = 1 to n4 step p4
                     for i5 = 1 to n5 step p5
                         if ok01 (i1,i2,i3, i4, i5)  then 
                            for i6 = 1 to n5 step p6
                                 if f(i1,i2,i3,i4,i5,i6) <   best_resultat  then       best_data = (.... )
                            next i6
                         end if
                     next i5
                 next i4
             end if
          next i3
       next i2
    next i1
    En vrai, j'ai même 11 boucles imbriquées. Et je recherche le minimum d'une certaine fonction.
    Dans ce genre de cas, le générateur peut directement être donné en paramètre de la fonction min. Exemple illustratif :
    min(((i, j) for i in (5, 7) for j in (4, 8)), key=lambda x: sum(x) % 10) vaut (7, 4) car, parmi les quatre couples possibles, c'est le couple (7, 4) dont la somme (11) a le plus petit reste de la division euclidienne par 10.

  5. #25
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Avril 2004
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 049
    Points : 1 380
    Points
    1 380
    Par défaut
    ça fait deux lignes en fait ...

  6. #26
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    J'aime bien le principe de ce calcul, mais ça date de 13 ans: peut-être pourrait-on faire mieux maintenant?
    Oui je pense...
    Code python : 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
    #!/usr/bin/env python3
    # coding: utf-8
     
    def genere_boucle(*boucles):
    	# Initialisation du résultat
    	res=[x[0] if len(x) > 1 else 0 for x in boucles]
     
    	# Travail de génération (présumé infini)
    	while True:
    		# On commence par retourner le résultat (qui fait partie des boucles en cours y compris et surtout pour le départ) 
    		yield res
     
    		# Traitement des boucles en partant de la fin vers le début
    		for i in range(len(boucles) - 1, -1, -1):
    			# On mémorise la taille de la boucle en cours (les boucles ont certains éléments facultatifs donc toutes les boucles n'ont pas la même taille)
    			l=len(boucles[i])
     
    			# On incrémente le résultat en cours de l'incrément de la boucle en cours (ou de 1 si pas d'incrément)
    			res[i]+=boucles[i][2] if l > 2 else 1
     
    			# Si le résultat est cohérent avec la borne max de la boucle on a fini l'incrément (on ne traite alors pas les autres boucles) 
    			if res[i] < boucles[i][1 if l > 1 else 0]: break
     
    			#  Le résultat ayant dépassé la valeur max, il est réinitialisé à la valeur de départ de la boucle (0 par défaut) et on passe à la boucle suivante (ou plutôt la précédente) 
    			res[i]=boucles[i][0] if l > 1 else 0
     
    		# Si on n'a jamais breaké...
    		else:
    			# C'est qu'on a traité toutes les boucles alors sortie du while
    			break
    		# for
    	# while
    # genere_boucle()
     
    for b in genere_boucle((1, 3), (1, 6, 2), (4,)): print(b)
    Déjà on gère la notion d'éléments facultatifs (comme borne min ou incrément) et surtout on évite le compteurs[-1] -= boucles[-1][2] initial pas forcément nécessaire...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #27
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour Sve@r,

    Merci pour l'amélioration du code! Quand je l'avais écrit, je n'avais que quelques mois de Python.

    Mais maintenant, suite à la proposition de wiztricks, mon meilleur code est celui qui utilise product de itertools. Voir mon message: https://www.developpez.net/forums/d2.../#post11780172. D'autant plus que la fonction proposée est facilement réutilisable dans d'autres projets.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  8. #28
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 985
    Points
    30 985
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Mais maintenant, suite à la proposition de wiztricks, mon meilleur code est celui qui utilise product de itertools.
    Je ne prétendais pas surclasser product(), j'ai juste vu ça comme un exercice de style pour se faire plaisir.
    Et en plus j'ai découvert que else: break (pourtant dans le for) faisait sortir de la boucle while située au niveau du dessus (au début j'avais fait comme toi avec un flag de fin à False puis j'ai voulu tester voir si ça marcherait et ça a fonctionné !!!). Ok je comprends l'idée (quand on est dans le "else" on a terminé (donc quitté) le for mais ça surprend quand-même un peu...)
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

Discussions similaires

  1. Comment vectoriser 14 boucles imbriquées ?
    Par Wicelo dans le forum R
    Réponses: 0
    Dernier message: 03/06/2013, 20h15
  2. éviter deux boucles imbriquées
    Par Décembre dans le forum MATLAB
    Réponses: 3
    Dernier message: 17/12/2010, 15h37
  3. [XL-2003] comment ecrire en vba des boucles imbriquées?
    Par doudou8mc dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 10/07/2009, 15h15
  4. Comment éviter des requêtes dans une boucle
    Par dam28800 dans le forum Langage
    Réponses: 43
    Dernier message: 04/12/2008, 16h53
  5. [MySQL] Comment éviter qu'une boucle While répète certains éléments ?
    Par matperino dans le forum PHP & Base de données
    Réponses: 6
    Dernier message: 01/06/2007, 10h11

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