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. #1
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut comment éviter plein de boucles imbriquées.
    J'ai des bases très faibles en Python, mais je me débrouille globalement en programmation.

    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.
    Je peux éliminer certaines combinaisons (les fonctions ok00 et ok01 .... qui font intervenir des fonctions trigo ou autres).
    Les pas p1, p2, p3 ... seraient tout petit à l'idéal, mais pour des raisons de performance, je vais devoir faire des impasses.

    En fonctionnant ainsi , je sais globalement faire.

    Mais je sais que ce n'est pas du tout 'Python' comme manière de procéder. Et que ça risque d'être atrocément long. Et quitte à faire comme ça, je ne vais pas utiliser Python.

    Je viens d'installer Python, pour faire un truc comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    aa = range(10)
    bb= range(15)
    dd = [[aa[i] , bb[j] , bb[k]  ] for i in range(len(aa) ) for j in range(len(bb) ) for k in range(len(bb) )    ]
    Et là, premier challenge, comment supprimer certaines lignes de dd, par exemple supprimer les lignes telles que x^3+ y^3+ 5*z^3 serait un multiple de 7 ?
    Ou comment ne pas les insérer au moment de créer dd !

    Et quand je vais exploiter dd , comment je vais récupérer dd.x, dd.y et dd.z ??


    Quand vous m'aurez aidé à passer cette étape, j'aurai forcément d'autres questions !
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Salut
    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
    Si les boucles imbriquées sont toutes identiques (même début, même fin, même step) alors tu peux utiliser itertools.product().

    Exemple
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for i1 in range(0, 10, 3):
    	for i2 in range(0, 10, 3):
    		for i3 in range(0, 10, 3):
    			print(i1, i2, i3)

    Ou bien...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    import itertools
    for x in itertools.product("".join(str(x) for x in range(0, 10, 3)), repeat=3):
    	(i1, i2, i3)=map(int, x)
    	print(i1, i2, i3)

    Citation Envoyé par tbc92 Voir le message
    Je viens d'installer Python, pour faire un truc comme ça :
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    aa = range(10)
    bb= range(15)
    dd = [[aa[i] , bb[j] , bb[k] ] for i in range(len(aa)) for j in range(len(bb)) for k in range(len(bb))]

    Et là, premier challenge, comment supprimer les lignes telles que x^3+ y^3+ 5*z^3 serait un multiple de 7 ?
    En Python on ne "supprime" pas X, on "garde" not(X).
    dd = [[aa[i], bb[j], bb[k]] for i in range(len(aa)) for j in range(len(bb)) for k in range(len(bb)) if ((aa[i]**3 + bb[i]**3 + 5*bb[k]**3) % 7) != 0].

    Citation Envoyé par tbc92 Voir le message
    Et quand je vais exploiter dd , comment je vais récupérer dd.x, dd.y et dd.z ??
    for d in dd: print(d[0], d[1], d[2]).
    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]

  3. #3
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    Citation Envoyé par tbc92 Voir le message
    Mais je sais que ce n'est pas du tout 'Python' comme manière de procéder. Et que ça risque d'être atrocément long. Et quitte à faire comme ça, je ne vais pas utiliser Python.
    Si j'écris:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    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
    je trie les ok000(i, j, k) pour i, j, k variant de... et ce sera d'autant plus long qu'il y aura de triplets (i, j, k) a tester.

    Mais dans ce cas, le soucis n'est pas du côté de Python mais de l'algo. (la première chose à optimiser).
    Côté Python on pourra améliorer l'esthétique en réduisant le nombre de lignes avec par exemple itertools.product.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> list(product(range(3), range(1,4,2)))
    [(0, 1), (0, 3), (1, 1), (1, 3), (2, 1), (2, 3)]
    >>>
    ce sera plus souple à coder mais peut être pas plus rapide.

    Citation Envoyé par tbc92 Voir le message
    Et là, premier challenge, comment supprimer certaines lignes de dd, par exemple supprimer les lignes telles que x^3+ y^3+ 5*z^3 serait un multiple de 7 ?
    Vous devriez savoir le faire avec un code basique: une boucle qui filtre... pour autant qu'on sache résoudre de façon algorithmique ce genre d'équation (sinon, pas la peine de se lancer à coder...).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  4. #4
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut
    Merci !!
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  5. #5
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut
    Pour supprimer les lignes introduites à tort, ou pour ne pas les insérer...

    On m'a expliqué un jour que dans Python, il faut éviter les boucles, et il faut 'factoriser' le code en manipulant des tableaux.

    En gros :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tableau3 = associe_tableaux(tableau1, tableau2, condition_filtre)
    Plutôt que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Pour tout élément1 de tableau1 
      Pour tout élemént2 de tableau2
        Si condition_filtre (element1, element2) alors insère dans tableau3.
    Je sais faire la boucle, j'ai fait ça des milliers de fois dans différents langages.

    J'ai tendance à croire que ce conseil qu'on m'a donné, c'est un très bon conseil. Et en tout cas, pour l'efficacité ou pour le fun, c'est ce que je vais essayer de respecter sur ce mini chantier.
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  6. #6
    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,

    Je me suis amusé il y a ... un certain temps (2008!) à trouver un moyen de créer des boucles imbriqués "à la volée" dans un programme, sans en connaitre le nombre au départ.

    Voilà la logique:

    On crée la liste des arguments de range des variables de boucles.
    Par exemple: boucles = [[1, 5, 2], [0, 3, 1], [1, 4, 1]] correspondrait à:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    for i in range(1, 5, 2):
        for j in range(0, 3, 1):
            for k in range(1, 4, 1):
                #...
                print(i, j, k)
                #...
    Dans la partie la plus profonde des boucles, on va calculer la valeur de tous les indices de boucles de la façon suivante:

    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
    boucles = [[1, 5, 2], [0, 3, 1], [1, 4, 1]]
     
    compteurs = [x[0] for x in boucles]  # => initialisation des compteurs de boucle (= valeur initiale de boucle)
    compteurs[-1] -= boucles[-1][2]  # le 1er incrément sera pour rien
    indmax = len(boucles)-1  # => index de la dernière boucle (la plus interne)
    finboucle = False  # => drapeau pour condition de fin de la boucle globale
     
    while True:
        # incrémentation des compteurs, test de boucle et condition de sortie
        for x in range(indmax, -1, -1):
            compteurs[x] += boucles[x][2]
            if compteurs[x]>=boucles[x][1]:
                if x==0:
                    finboucle = True
                    break
                compteurs[x] = boucles[x][0]
            else: break
        if finboucle: break
     
        # =====> partie utile de la boucle <=====
        # ...
        print(compteurs)
        # ...
    L'exécution donne:

    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
    [1, 0, 1]
    [1, 0, 2]
    [1, 0, 3]
    [1, 1, 1]
    [1, 1, 2]
    [1, 1, 3]
    [1, 2, 1]
    [1, 2, 2]
    [1, 2, 3]
    [3, 0, 1]
    [3, 0, 2]
    [3, 0, 3]
    [3, 1, 1]
    [3, 1, 2]
    [3, 1, 3]
    [3, 2, 1]
    [3, 2, 2]
    [3, 2, 3]
    Dans chacune de ces listes, vues dans la partie la plus profonde des boucles, le 1er élément donne i, le second j et le 3ème k. Ce qui veut dire que les indices des boucles s'obtiennent par: compteurs[0], compteurs[1], compteurs[2]

    On voit bien la progression des indices de boucles.

    Plus d'infos sur mon site: https://python.jpvweb.com/python/mes...les_imbriquees

    J'aime bien le principe de ce calcul, mais ça date de 13 ans: peut-être pourrait-on faire mieux maintenant?
    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

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    Citation Envoyé par tbc92 Voir le message
    J'ai tendance à croire que ce conseil qu'on m'a donné, c'est un très bon conseil. Et en tout cas, pour l'efficacité ou pour le fun, c'est ce que je vais essayer de respecter sur ce mini chantier.
    Un tableau se réalise de tas de façons et tout dépend de comment est faite la fonction "associe_tableaux".

    Sûr que faire des calculs de matrices avec des listes de listes de base sera plus lent que de le faire faire par numpy (dont pas mal de code ont été optimisés en C).

    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?
    Avec product, écrit sur un coin de table:
    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
    >>> from itertools import product
    >>> boucles = [[1, 5, 2], [0, 3, 1], [1, 4, 1]]
    >>> for z in product(*[range(*v) for v in boucles]):
    ...     print(z)
    ...
    (1, 0, 1)
    (1, 0, 2)
    (1, 0, 3)
    (1, 1, 1)
    (1, 1, 2)
    (1, 1, 3)
    (1, 2, 1)
    (1, 2, 2)
    (1, 2, 3)
    (3, 0, 1)
    (3, 0, 2)
    (3, 0, 3)
    (3, 1, 1)
    (3, 1, 2)
    (3, 1, 3)
    (3, 2, 1)
    (3, 2, 2)
    (3, 2, 3)
    >>>
    et non testé pour voir si c'est plus rapide mais en tous cas, c'est plus court...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    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 wiztricks

    Effectivement, avec product d'itertools, c'est plus court!

    Merci!
    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

  9. #9
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    On m'a expliqué un jour que dans Python, il faut éviter les boucles, et il faut 'factoriser' le code en manipulant des tableaux.

    En gros :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tableau3 = associe_tableaux(tableau1, tableau2, condition_filtre)
    Plutôt que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Pour tout élément1 de tableau1 
      Pour tout élemént2 de tableau2
        Si condition_filtre (element1, element2) alors insère dans tableau3.
    Tout à fait. En fait la raison principale c'est que l'opération "insere dans tableau" est assez gourmande (l'insertion force le décalage de ce qui suit et ça...). Ok il y a l'opération "append()" qui insère à la fin et donc qui est moins gourmande mais tout de même...

    Donc ce qui en alrgorithme classique s'écrirait...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    res=[]
    for x in orig:
    	if test(x) == v:
    		res.append(fct(x))
    s'écrira dans ce que tu nommes "factorisation" et qui se nomme en réalité les "listes en compréhension" res=[fct(x) for x in orig if test(x) == v]. Dans cette écriture on crée la liste final d'un seul bloc "à la volée".

    Appliqué à ton exemple, cela serait tableau_3=[... (ce qu_il faut mettre dans tableau_3)... for element_1 in tableau1 for element_2 in tableau_2 if condition_filtre(element1, element2)].

    Si tu veux je me suis amusé à écrire un petit benchmark pour comparer le append() via la liste en comprehension
    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
    36
    37
    38
    #!/usr/bin/env python3
    # coding: utf-8
     
    import random
    import timeit
    from functools import partial
     
    # La fonction qui testera chaque élément de la liste
    def test(x): return x > 100
     
    # La fonction qui crée la liste par append()
    def fct_append(l):
    	res=list()
    	for x in l:
    		if test(x): res.append(x)
    # fct_append()
     
    # La fonction qui passe par les listes en compréhension
    def fct_comprehension(l): return list(x for x in l if test(x))
     
    # Les fonctions à tester (on évitera la lambda pour être le plus impartial possible)
    fct={
    	"append" : fct_append,
    	"comprehension" : fct_comprehension,
    }
     
    # Initialisation random
    random.seed()
     
    # La liste témoin
    temoin=list(range(10_000_000))
     
    # Appel des fonctions dans un ordre aléatoire et affichage du chrono
    print("start", len(temoin))
    for (k, v) in random.sample(fct.items(), len(fct)):
    	t=timeit.Timer(partial(v, temoin)).repeat(repeat=10, number=20)
    	print("%s: min=%f, max=%f, avg=%f" % (k, min(t), max(t), sum(t)/len(t)))
    # for

    Et un résultat que j'ai eu (attention il faut attendre 6mn)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    start 10000000
    append: min=16.679654, max=17.124029, avg=16.880704
    comprehension: min=15.728750, max=15.972923, avg=15.836860
    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]

  10. #10
    Membre émérite

    Homme Profil pro
    Ingénieur calcul scientifique
    Inscrit en
    Mars 2013
    Messages
    1 229
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur calcul scientifique

    Informations forums :
    Inscription : Mars 2013
    Messages : 1 229
    Points : 2 328
    Points
    2 328
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    On m'a expliqué un jour que dans Python, il faut éviter les boucles, et il faut 'factoriser' le code en manipulant des tableaux.
    Les tableaux en tant que tel n'existent pas en python. Soit on parle de liste de liste, et dans ce cas là, et bien vous n'avez pas le choix, il faut itérer sur les indices, soit vous utilisez numpy, et dans ce cas là si A et B sont deux tableaux de même taille, alors numpy vous permet d'écrire A+B sans avoir à parcours tous les indices de chaque tableau. Et là je prend A+B en exemple, mais il y a beaucoup d'autres opérations que numpy vous permet de faire sur les tableaux entiers (que numpy appelle des arrays).

    Ensuite vous présentez un problème, avec 2 aspects qui sont différents. D'un côté vous voulez une écriture plus compacte pour une quinzaine de boucle imbriquée. De l'autre vous voulez un qqch qui ne soit pas trop lent à exécuter. Ce sont 2 choses différentes, et les codes les plus courts ne sont pas forcément les plus rapides.

    Si j'en reviens à votre besoin initial, vous chercher le minimum d'une fonction. Et donc en fait les boucles imbriquées, ce n'est pas ca votre problème.
    Pourquoi vous amusez vous à parcourir l'espace des paramètres pour trouver le minimum de la fonction, alors que vous avez des choses dans scipy qui sont déjà faites pour ca (googliser scipy optimize vous allez voir plein de chose). Et si vous rentrer un peu dans le détail de ce que font ces méthodes, vous allez voir que ce n'est pas de la recherche de minimum exhaustif, mais plutôt des choses basé sur des méthode de descente, avec le risque d'obtenir un minimum local et non global. Mais ca, ca peut se corriger, en faisant une petite phase d'initialisation soit en MonteCarlo, soit sur une grille de l'espace des paramètres assez grossière. Tout dépend après de comment votre fonction est fichue : est-elle bien régulière (mathématiquement parlant), sans discontinuité ni saut brusque de valeur ? Il vous faut quoi comme précision/incertitude sur ce minimum ?

  11. #11
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut
    J'imagine que ce sont des milli-secondes, mais peu importe en fait.
    L'écart entre 15.8 et 16.9 n'est pas non plus révolutionnaire , environ 6%

    Par curiosité (je pourrais faire les tests moi-même, mais ce week-end seulement), si la fonction test() est très compliquée, l'écart va augmenter (plus de 6%) ou diminuer ?

    Dans ce que je comprends, la méthode par compréhension fait que tous les NEXT i implicites, dans les boucles, sont compilés, et non plus interprétés. Et c'est là le principal bénéfice sur ce mini-exemple.

    Et dans la commande : return list(x for x in l if test(x))
    A chaque fois qu'on passe sur cette instruction, la fonction list() a besoin d'avoir un truc compilé, donc list commence par compiler la fonction test(), puis peut exécuter la fonction test-compilée() plein de fois pour tous les éléments x, au lieu d'exécuter une fonction test-non-compilée plein de fois dans la méthode classique avec des boucles

    Du coup, si mon interprétation est bonne, le bénéfice sera en principe plus important (en pourcentage du temps total de traitement) si la fonction test() est assez compliquée.

    Et de toutes façons, c'est le nerf de la guerre, la fonction test() doit être aussi bien écrite que possible.
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  12. #12
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut
    Je vais regarder la piste scipy, je n'y avais même pas pensé.
    Un de mes soucis, c'est que je sais d'avance que les points 'optimum' sont à la frontière du domaine de définition.

    J'ai les fonctions que j'avais appelées ok00() ... ok01() dans mon premier message.
    En fait si ok00(x,y,z) est strictement négatif, je suis en dehors du domaine de définition
    Et le ou les points recherchés vérifient ok00(x,y,z) = 0

    Et je réponds tout de suite à l'objection ... je ne peux pas inverser mes fonctions ok00(), ok01() etc

    Plus précisément, si je me limite à des entiers, alors les points optimums ne vérifient pas ok00(x,y,z)=0 ; ma fonction est définie sur une partie de [0,1]^11, et je la modifie pour travailler sur des entiers. Les points que je vais trouver au final sont donc proches de la frontières, et pas sur la frontière.

    En dehors de la contrainte liée au domaine de définition, la fonction a toutes les qualités requises pour des méthodes de descente de gradient.

    C'est en fait ce que je comptais plus ou moins faire ici. Un premier traitement pour rechercher quelques points 'plutôt bons', avec un maillage de [0,1]^11 pas très fin. Et autour des points les plus prometteurs, recommencer, avec un maillage plus fin, et rebelote éventuellement.

    Donc ... oui , je vais de suite regarder vers scipy.
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    J'imagine que ce sont des milli-secondes, mais peu importe en fait.
    Ce sont des secondes. Le Timer boucle "repeat" fois et appele la fonction demandée "number" fois avec chrono. Le min/max/moyenne final sont donc le min/max/moyenne sur chaque "repeat" fois (ce "chaque" ayant lui appelé "number" fois la fonction testée). L'ensemble du test prend à peu près 6mn. Ensuite tu peux jouer avec "repeat" ou "number" pour amplifier ou diminuer tout ça.

    Citation Envoyé par tbc92 Voir le message
    Par curiosité (je pourrais faire les tests moi-même, mais ce week-end seulement), si la fonction test() est très compliquée, l'écart va augmenter (plus de 6%) ou diminuer ?
    En fait j'en sais rien. C'est sur un autre fil où on parlait des listes en compréhension qu'un autre intervenant a donné cet exemple de test que j'ai repris sans trop réfléchir. Parce que si on réfléchit, le but initial étant de mesurer l'impact du append(), plus on aura de append() plus la mesure sera fiable. Donc à la réflexion le mieux serait de carrément supprimer le test...(à voir)

    Citation Envoyé par tbc92 Voir le message
    Dans ce que je comprends, la méthode par compréhension fait que tous les NEXT i implicites, dans les boucles, sont compilés, et non plus interprétés. Et c'est là le principal bénéfice sur ce mini-exemple.
    Euh... ouais ouais ouais... Bon déjà parler de "next i" en Python a peu de sens. Le "next i" (si on prend celui du basic) correspond grosso-modo à un incrément de i. Or le for en Python est une itération sur une liste de valeurs existantes et non calculées. Ou (dit autrement), une boucle de 0 à 10 en C sera du type for (i=0; i < 10; i++) (donc un calcul et une évaluation) alors qu'en Python elle sera du type for i in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) donc aucun calcul, les termes à traiter sont déjà présents. Simplement il existe ensuite des outils pour "générer" ces termes (ie le range()) mais une fois générés, ils sont là (ou présumés là).

    Citation Envoyé par tbc92 Voir le message
    Et dans la commande : return list(x for x in l if test(x))
    A chaque fois qu'on passe sur cette instruction, la fonction list() a besoin d'avoir un truc compilé, donc list commence par compiler la fonction test(), puis peut exécuter la fonction test-compilée() plein de fois pour tous les éléments x, au lieu d'exécuter une fonction test-non-compilée plein de fois dans la méthode classique avec des boucles
    Euh... ouais ouais ouais... non en fait non non non. Déjà Python c'est du pseudo-compilé (va faire un tour dans tes sources tu devrais voir des fichiers ".pyc" voire même des dossiers "__pycache__" qui contiennent le même code mais compilé) donc la fonction est compilée.
    Et en fait dans les deux cas elle est appelée et exécutée.
    Démo
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def test(x):
    	print(x, id(x))
     
    tab=range(500, 505)
     
    print("A")
    for i in tab:
    	test(i)			# sous-entendu if test(i) tab2.append(i) mais ici on regarde juste la fonction
     
    print("B")
    [test(i) for i in tab]
    (je prends des nombres volontairement élevés car les nombres compris entre -5 et 256 sont (je schématise) déjà intégrés dans Python et donc toute variable valant un de ces nombres aura toujours le même id())

    Et le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    A
    500 139635129486224
    501 139635127895600
    502 139635129486224
    503 139635127895600
    504 139635129486224
    B
    500 139635127895600
    501 139635127895664
    502 139635127895600
    503 139635127895664
    504 139635127895600
    Explication: chaque appel de test() génère un nouveau "x" qui prend un nouvel id(). A ce moment là, le "x" de l'itération précédente n'a plus besoin d'exister. Il est alors récupéré par le gc de Python et son id redevient disponible. Il sera alors réutilisé à l'itération suivante. C'est pour ça que tous les id() sont les même à deux rang d'écart.

    Citation Envoyé par tbc92 Voir le message
    Du coup, si mon interprétation est bonne, le bénéfice sera en principe plus important (en pourcentage du temps total de traitement) si la fonction test() est assez compliquée.
    Ben à mon avis non. Ceci dit ça se teste (rien ne t'interdit de t'y mettre )

    Citation Envoyé par tbc92 Voir le message
    Et de toutes façons, c'est le nerf de la guerre, la fonction test() doit être aussi bien écrite que possible.
    Rappel: on parlais du append() sur une liste, pas du test d'un élément...
    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]

  14. #14
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 278
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 278
    Points : 12 726
    Points
    12 726
    Par défaut
    Et pourquoi pas, toujours avec product, un truc du genre (ici, on a l'équivalent de 3 boucles imbriquées) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    from itertools import product
    for i1,i2,i3 in product(range(1,10,3),range(2,19,4),range(3,12,3)):
        print(i1,i2,i3)
    Cordialement.

  15. #15
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 053
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 053
    Points : 9 392
    Points
    9 392
    Par défaut
    aa = product(range(1,10,3),range(2,19,4),range(3,12,3)) : j'avais bien capté.
    for i1,i2, i3 in product(range(1,10,3),range(2,19,4),range(3,12,3)): je n'avais pas cette syntaxe, peut-être que ...
    N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.

  16. #16
    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,

    En partant du code proposé par wiztricks ici (https://www.developpez.net/forums/d2.../#post11779784), on peut créer une petite fonction utilitaire facilement réutilisable, qui appellera dans la partie la plus imbriquée une fonction callback avec l'état des indices de boucle.

    La fonction appelée comme callback (ici "test") pourra comporter tout ce qu'on doit faire dans la boucle la plus imbriquée.

    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
    from itertools import product
     
    ##############################################################################
    def fnboucles(boucles, callback):
        """Simule des boucles imbriquées selon la liste boucles donnant les 
               arguments des "range" des boucles successives
           callback est la fonction appelée dans la boucle la plus imbriquée avec 
               comme argument l'état des indices de boucle
        """
        for z in product(*[range(*v) for v in boucles]):
            callback(z)
     
    ##############################################################################
    def test(z):
        print(z)
     
    ##############################################################################
    boucles = [[1, 5, 2], [0, 3, 1], [1, 4, 1]]
     
    fnboucles(boucles, callback=test)
    Qui affichera:

    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
    (1, 0, 1)
    (1, 0, 2)
    (1, 0, 3)
    (1, 1, 1)
    (1, 1, 2)
    (1, 1, 3)
    (1, 2, 1)
    (1, 2, 2)
    (1, 2, 3)
    (3, 0, 1)
    (3, 0, 2)
    (3, 0, 3)
    (3, 1, 1)
    (3, 1, 2)
    (3, 1, 3)
    (3, 2, 1)
    (3, 2, 2)
    (3, 2, 3)
    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

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    for i1,i2, i3 in product(range(1,10,3),range(2,19,4),range(3,12,3)): je n'avais pas cette syntaxe, peut-être que ...
    C'est une syntaxe issue du unpacking => (a, b, c)=(1, 2, 3).
    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]

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

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 048
    Points : 1 378
    Points
    1 378
    Par défaut
    salut,
    j'ai pas lu tout le fil mais pour les imbrications il y a possiblement les lambda.

    un exemple bidon:

    Code : 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,21)]
    l2 = lambda *i:[l3(*i+(j,))    for j in range(1,11) if i[0]!=11]
    l3 = lambda *i:[print(*i+(j,)) for j in range(1, 5) if i[1]//i[0]]
     
    l1()

  19. #19
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Ben en fait les exemples illustratifs de ce fil sont sur 3 niveaux mais l'idée générale était d'éviter une imbrication à n niveaux (n étant donc variable).
    Ton exemple me semble un peu trop lié à seulement 3 niveaux.
    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]

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

    Informations forums :
    Inscription : Avril 2004
    Messages : 1 048
    Points : 1 378
    Points
    1 378
    Par défaut
    Il faut autant de lambda que de niveau. L'avantage est que c'est facile à lire et à écrire.

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