2017-10-12 3 views
1

J'essaye de trouver toutes les permutations d'une liste d'entiers d'une gamme spécifique en utilisant la récursivité. Par exemple, si lst = [0,1,2], un appel à def permute(lst, 0, 1) doit renvoyer [[0,1], [1,0]] dans ce format. De même, un appel à permute(lst, 0, 2) doit renvoyer [[0,1,2], [0,2,1]...].Prise en compte des indices lors de la recherche de permutations de list int en utilisant la récursivité en python?

Jusqu'à présent, mon code ne fonctionne que pour trouver des permutations d'une liste complète, de l'index 0 à len (LST):

def permute(lst, low, high): 
    if low == high: 
     print(lst) 
    for index in range(low, high + 1): 
      lst[index], lst[low] = lst[low], lst[index] 
      permute(lst, low + 1, high) 
      lst[index], lst[low] = lst[low], lst[index] 

low = 0 et high est le len(lst).

Si je change les indices dans ce code, je n'obtiens pas une sortie correcte. Toute suggestion sur la façon de prendre en compte les indices?

+0

Vous modifiez 'lst', vous devez probablement créer une nouvelle liste pour conserver la permutation et la' renvoyer '. Pourquoi utiliser la récursivité? – AChampion

+0

Je ne pense pas que cela puisse être fait facilement, sans détruire l'élégance de l'algorithme. Vous pouvez probablement le faire en donnant à 'permute' un argument supplémentaire qui maintient l'argument' low' d'origine, mais c'est un peu maladroit. La solution simple est d'avoir une autre fonction qui joue le rôle d'interface avec votre fonction 'permute' récursive. Vous appelez donc la fonction d'interface et elle appelle 'permute' avec une partie de la liste. –

+0

Si vous n'avez pas besoin de récursion, vous pouvez utiliser 'itertools.permutation (lst [low: high + 1])'. – Eric

Répondre

0

Vous pouvez le faire avec une fonction récursive intérieure, .: par exemple

def permute(lst, start, end): 
    def permutations(l): 
     if len(l) <= 1: 
      return [l] 
     a = [] 
     for p in permutations(l[:-1]): 
      for i in range(len(l)): 
       a.append(p[i:] + [l[-1]] + p[:i]) 
     return a 
    return permutations(lst[start:end+1]) 

In [] 
lst = [0,1,2,3,4] 
permute(lst, 0, 1) 

Out[]: 
[[0, 1], [1, 0]] 

In [] 
permute(lst, 0, 2) 

Out[]: 
[[0, 1, 2], [1, 2, 0], [2, 0, 1], [1, 0, 2], [0, 2, 1], [2, 1, 0]] 
+0

Un inconvénient mineur de l'utilisation d'une fonction imbriquée est que la fonction interne est redéfinie chaque fois que la fonction externe est appelée, plutôt que d'être définie une fois, lorsque le script démarre. Mais c'est une fonction assez petite, donc j'espère que ce n'est pas une grosse affaire. –

+0

Bon point, il n'y a pas besoin que ce soit une fonction interne autre que de le cacher - il n'y a pas de fermeture, donc il suffit d'avoir 2 fonctions globales. – AChampion

0

Voici une version de votre code qui utilise un argument supplémentaire pour rappeler la position de départ de la liste. J'ai changé votre fonction en un générateur récursif, donc il donne les valeurs, plutôt que de les imprimer. J'ai également changé high à stop, ce qui est compatible avec la convention utilisée dans le découpage de Python par exemple, seq[start:stop], et range(start, stop).

def permute(lst, start, stop, low=None): 
    if low == stop - 1: 
     yield lst[start:stop] 
    if low is None: 
     low = start 
    for index in range(low, stop): 
      lst[index], lst[low] = lst[low], lst[index] 
      for t in permute(lst, start, stop, low + 1): 
       yield t 
      lst[index], lst[low] = lst[low], lst[index] 

# Test 
lst = list(range(5)) 
print(list(permute(lst, 1, 4))) 

for t in permute(lst, 0, 3): 
    print(t) 

sortie

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

Ce code fonctionne de façon identique sur Python 2 & 3, bien que dans Python 3, il peut être un peu plus efficace (et plus compact) en utilisant le yield from syntaxe. Remplacez cette for t in permute ... boucle avec

yield from permute(lst, start, stop, low + 1) 

En Python 3, vous pouvez également créer une liste des valeurs d'une manière plus compacte:

[*permute(lst, 1, 4)] 

Enfin, voici un autre générateur récursif construit de votre algorithme, mais il permute toute la liste. Bien sûr, vous pouvez l'appeler avec une fonction d'interface pour permuter une sous-liste.

def permuter(lst): 
    if not lst: 
     yield lst 
    for index in range(len(lst)): 
     lst[index], lst[0] = lst[0], lst[index] 
     first = lst[:1] 
     for t in permuter(lst[1:]): 
      yield first + t 
     lst[index], lst[0] = lst[0], lst[index] 

def permute(lst, start, stop): 
    yield from permuter(lst[start:stop]) 

lst = list(range(5)) 
print(list(permute(lst, 1, 4))) 
+0

Utile, mais je cherchais une solution qui ne change pas les paramètres d'origine def permute (lst, low, high) – sweeper123

+0

@ sweeper123 Comme je l'ai dit dans le commentaire sur votre question, je ne pense pas qu'il soit possible de le faire sans avoir un moyen de suivre le «faible» d'origine. Nous n'avons pas besoin de faire cela avec un argument supplémentaire, mais c'est un moyen pratique; une autre option (en dehors de l'utilisation d'un global, qui serait grossier) consiste à utiliser un attribut de fonction. –

+0

@ sweeper123 Et bien sûr, il vaut mieux éviter la récursivité en Python si vous n'en avez pas vraiment besoin (par exemple lors du traitement de structures de données récursives) car contrairement à d'autres langages, Python ne peut optimiser la récursivité. Et cela a une limite à la profondeur de la récursivité, bien que cela ne soit pas un problème ici, car si vous essayez de permuter une liste de 1000 éléments, vous aurez d'autres problèmes avant d'atteindre la limite de récursion, comme la mort thermique de l'univers. ;) –