1
votes

Python - Boucles imbriquées paramétriques avancées

J'ai trouvé une excellente réponse sur les boucles imbriquées paramétriques - Boucles imbriquées paramétriques en Python Cependant, mon exigence est un peu plus difficile

Problème :

Il s'agit d'une boucle imbriquée à 3 profondeurs (je voudrais une fonction qui a n- depth):

for i in range(100 + 1):
    for j in range(i, 100 + 1):
        for k in range(j, 100 + 1):
            -> need to retrieve [i,j,k]

Notez que le point de départ de chaque boucle est dynamique et change avec chaque boucle parente


2 commentaires

Je ne suis pas sûr de comprendre, pourquoi pas print (i, j, k) ?


@ olinox14 La question porte sur l'imbrication profonde arbitraire, le code à 3 profondeurs est un exemple.


4 Réponses :


2
votes

Cela peut être fait de manière récursive (comme vous l'avez déjà deviné), par exemple de cette manière:

def nest_gen(count, start=0, end=101):
    if count < 1:
        return
    elif count == 1:
        yield from ((r,) for r in range(start, end))
        return

    for i in range(start, end):
        yield from ((i,) + r for r in nest_gen(count - 1, i, end))


print(tuple(nest_gen(6, end=5)))


3 commentaires

J'aime cette solution plus que ma réponse - c'est exactement la même logique, mais beaucoup plus pythonique


Fonction non totale. Provoque un débordement de pile lorsque count = 0 .


Vous êtes le bienvenu mais le codomain n'est toujours pas total. Veuillez consulter le commentaire que j'ai fait sur le post de Green Cloak pour plus de détails.



2
votes

C'est le genre de chose pour laquelle j'utiliserais la récursivité - comme je vois que vous l'avez noté dans votre balise. Quelque chose comme ça, par exemple:

[(0,0,0), (0,0,1), (0,0,2), ..., (0,0,100), (0,1,1), ..., (0,100,100), (1,1,1), ..., (99,99,99), (100,100,100)]

qui, lorsqu'il est appelé comme iterate (3) devrait produire

def iterate(depth, i=0, maxrange=101):
    if depth <= 0:
        return (yield ())
    for j in range(i, maxrange):                    # for each value of j...
        if depth == 1:                              # base case:
            yield (j,)                              #    return a 1-tuple
        else:                                       # recursive case:
            for k in iterate(depth-1, j, maxrange): #    get a generator for the next level of recursion
                yield (j,) + k                      #    yield all (depth+1)-tuples from prepending j to the next layer of recursion

p>


6 commentaires

erreur de syntaxe sur votre première ligne, : manquant. le programme donne un mauvais résultat lorsque depth = 0 est spécifié.


Merci de l'avoir signalé, j'ai fait de mon mieux pour y remédier


c'est proche mais le codomain (sorties possibles) est un peu off. Vous voulez probablement return (yield ()) sinon depth = 0 donne aucun résultat au lieu d'un résultat vide . Pour comprendre pourquoi c'est un problème, imaginez si subtract (5,5) ne renvoie rien ( None ) au lieu de 0 , le "vide" numéro. La fonction dépendant de la sortie de soustraire devrait vérifier si elle a reçu une valeur. En renvoyant une valeur "vide", l'appelant de soustraire est assuré qu'un résultat sera toujours retourné.


@ user633183 Merci, je l'ai changé en conséquence. Le retour de tuple () fonctionnerait-il également?


return (yield tuple ()) serait équivalent, oui.


@ user633183 Ce qu'un retour s d'un générateur peut être complètement différent (et est généralement moins intéressant que) ce qu'il produit s. Je ne comprends pas pourquoi il doit répondre à une exigence particulière ici.



2
votes

Voici une approche itérative:

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]
[0, 1, 1]
[0, 1, 2]
[0, 1, 3]
[0, 2, 2]
[0, 2, 3]
[0, 3, 3]
[1, 1, 3]
[1, 2, 2]
[1, 2, 3]
[1, 3, 3]
[2, 2, 3]
[2, 3, 3]
[3, 3, 3]

un exemple:

for i in iterate(4, 3):
    print(i)

donne:

def iterate(max_range, dim):
    if dim == 0:  #handle edge case
        yield from iter(())
    elif dim == 1:
        yield [0]
    else:
        fields = [0]*dim
        while True:
            yield fields
            fields[-1] += 1
            for i in reversed(range(1, dim)):
                if fields[i] >= max_range:
                    fields[i - 1] += 1
                    fields[i] = min(fields[i - 1], max_range -1)
            if fields[0] == max_range:
                break


3 commentaires

Je ne pense pas que cela corresponde aux exigences du PO. Le niveau d'imbrication et la plage max sont deux variables distinctes.


Belle mise à jour mais un problème demeure. Si dim = 0 votre programme renvoie une erreur au lieu de renvoyer un résultat valide. Voir les commentaires dans la réponse de GreenCloak pour plus de détails.


Si vous considérez dim = 0 comme une entrée valide, vous avez raison. Mais il ne devrait pas être trop difficile de vérifier dim == 0 et de renvoyer un itérateur vide ou tout ce qui est considéré comme significatif dans ce cas.



2
votes

Voici une approche récursive utilisant un argument par défaut. Les points numérotés ci-dessous font référence aux commentaires numérotés dans le code.

  1. (base) La profondeur est nulle. Nous avons terminé, alors donnez la combinaison, comb
  2. (inductif) La profondeur est d'au moins 1. Pour chaque x d'une plage, déléguez au générateur récursif en utilisant x comme nouveau point de départ pour la plage imbriquée et ajoutez x à la combinaison, comb .
for p in nested_range (5, 0, 3):
  print(p)

# (0, 0, 0, 0, 0)
# (0, 0, 0, 0, 1)
# (0, 0, 0, 0, 2)
# (0, 0, 0, 1, 1)
# (0, 0, 0, 1, 2)
# (0, 0, 0, 2, 2)
# (0, 0, 1, 1, 1)
# (0, 0, 1, 1, 2)
# (0, 0, 1, 2, 2)
# (0, 0, 2, 2, 2)
# (0, 1, 1, 1, 1)
# (0, 1, 1, 1, 2)
# (0, 1, 1, 2, 2)
# (0, 1, 2, 2, 2)
# (0, 2, 2, 2, 2)
# (1, 1, 1, 1, 1)
# (1, 1, 1, 1, 2)
# (1, 1, 1, 2, 2)
# (1, 1, 2, 2, 2)
# (1, 2, 2, 2, 2)
# (2, 2, 2, 2, 2)

Voici une plage imbriquée de trois (3) niveaux de profondeur -

for p in nested_range (0, 0, 4):
  print(p)

# ()

Cette implémentation est une fonction totale et fournit un résultat valide lorsque depth = 0 -

for p in nested_range (3, 0, 4):
  print(p)

# (0, 0, 0)
# (0, 0, 1)
# (0, 0, 2)
# (0, 0, 3)
# (0, 1, 1)
# (0, 1, 2)
# (0, 1, 3)
# (0, 2, 2)
# (0, 2, 3)
# (0, 3, 3)
# (1, 1, 1)
# (1, 1, 2)
# (1, 1, 3)
# (1, 2, 2)
# (1, 2, 3)
# (1, 3, 3)
# (2, 2, 2)
# (2, 2, 3)
# (2, 3, 3)
# (3, 3, 3)

Et pour faire bonne mesure, voici la sortie d'une plage imbriquée de cinq (5) niveaux de profondeur - p>

def nested_range (depth = 0, start = 0, end = 1, comb = ()):
  if depth == 0:
    yield comb                  #1
  else:
    for x in range(start, end): #2
      yield from nested_range(depth - 1, x, end, comb + (x,))


0 commentaires