Remplacer emboîtés Pour boucles ... ou non
-
20-08-2019 - |
Question
J'ai un script qui boucle une série de quatre (ou moins) chaînes de caractères. Par exemple:
aaaa
aaab
aaac
aaad
Si ont été en mesure de mettre en œuvre avec des boucles imbriquées comme ceci:
chars = string.digits + string.uppercase + string.lowercase
for a in chars:
print '%s' % a
for b in chars:
print '%s%s' % (a, b)
for c in chars:
print '%s%s%s' % (a, b, c)
for d in chars:
print '%s%s%s%s' % (a, b, c, d)
est ce genre de boucle nichant une mauvaise chose, et si oui, quelle serait une meilleure façon d'accomplir ce que je fais?
La solution
import string
import itertools
chars = string.digits + string.letters
MAX_CHARS = 4
for nletters in range(MAX_CHARS):
for word in itertools.product(chars, repeat=nletters + 1):
print (''.join(word))
Ce imprimerons tous les 15018570
mots que vous recherchez. Si vous voulez plus / moins de mots changent simplement la variable MAX_CHARS
. Il aura encore seulement deux s pour tout for
nombre de caractères, et vous ne devez pas vous répéter. Et est assez lisible. .
Autres conseils
Je vais soumettre ma réponse comme le plus lisible et moins évolutive:)
import string
chars = [''] + list(string.lowercase)
strings = (a+b+c+d for a in chars
for b in chars
for c in chars
for d in chars)
for string in strings:
print string
EDIT: En fait, ce qui est faux, car il produira des copies de toutes les chaînes de longueur <4. Retrait de la chaîne vide de la gamme chars
serait tout simplement produire des chaînes de 4 caractères.
Normalement, je supprime cette réponse, mais je reste un peu comme si vous avez besoin de générer des chaînes de la même longueur.
Ecrire pour le programmeur premier -. Le second ordinateur
S'il est clair et évident de comprendre alors son correcte.
Si les questions de vitesse et le compilateur ne permet pas d'optimiser toute façon et si vous mesurez et il est le problème - alors penser à une façon plus rapide intelligent
Je ne pense pas que ce soit une mauvaise chose, à condition que vous comprenez (et le document :-) il. Je ne doute pas qu'il peut y avoir une façon ou d'une solution plus intelligente pythonique (avec lambdas ou autres joyeusetés) mais je l'ai toujours privilégié la lisibilité sur l'intelligence.
Puisque vous devez générer toutes les possibilités de 1, 2, 3 et 4 caractères « mots », cette méthode est aussi bon que. Je ne sais pas combien de temps cela prendrait puisque vous générer efficacement (très approximativement) 14 millions de lignes de sortie (mais probablement toute solution aurait ce problème).
Pré-calcul des préfixes communs peuvent donner un coup de pouce de la vitesse, mais vous seriez mieux mesurer pour vérifier ( toujours vérifier, jamais supposer):
chars = string.digits + string.uppercase + string.lowercase
for a in chars:
print a
for b in chars:
ab = '%s%s' % (a, b)
print ab
for c in chars:
abc = '%s%s' % (ab, c)
print abc
for d in chars:
print '%s%s' % (abc, d)
EDIT: En fait, je l'ai fait quelques points de référence (avec Windows Python 2.6.1) - cette version prend environ 2,25 unités de temps par rapport à la 2.84 il est donc 26% plus rapide d'origine. Je pense que cela pourrait justifier son utilisation (encore une fois, tant qu'il est documenté clairement ce qu'il tente d'accomplir).
@ et de nosklo @Triptych's produisent des résultats différents:
>>> list(map(''.join, itertools.chain.from_iterable(itertools.product("ab",
... repeat=r) for r in range(4)))) # @nosklo's
['', 'a', 'b', 'aa', 'ab', 'ba', 'bb', 'aaa', 'aab', 'aba', 'abb', 'baa', 'bab', 'bba', 'bbb']
>>> ab = ['']+list("ab")
>>> list(map(''.join, (a+b+c for a in ab for b in ab for c in ab)))
['', 'a', 'b', 'a', 'aa', 'ab', 'b', 'ba', 'bb', 'a', 'aa', 'ab', 'aa', 'aaa', 'aab', 'ab', 'aba', 'abb', 'b', 'ba', 'bb', 'ba', 'baa', 'bab', 'bb', 'bba', 'bbb']
Ici se modifié @ solution de Triptyque qui produisent le même résultat que celui de l'@ nosklo:
>>> ab = "ab"
>>> list(map(''.join, itertools.chain([''], ab, (a+b for a in ab for b in ab),
... (a+b+c for a in ab for b in ab for c in ab))))
<*> Il existe de nombreux algorithmes pour générer toutes les permutations d'un ensemble. Ce que vous voulez ici est un problème connexe, mais pas directement analogue. Suggestions de lecture
Il ne répond pas exactement à la question, mais cela retournerait la combinaison pour la th n
longueur maximale et des caractères donnés dans l'alphabet à utiliser:
#!/usr/bin/python
def nth_combination(n, maxlen=4, alphabet='abc'):
"""
>>> print ','.join(nth_combination(n, 1, 'abc') for n in range(3))
a,b,c
>>> print ','.join(nth_combination(n, 2, 'abc') for n in range(12))
a,aa,ab,ac,b,ba,bb,bc,c,ca,cb,cc
>>> import string ; alphabet = string.ascii_letters + string.digits
>>> print ','.join(nth_combination(n, 4, alphabet) for n in range(16))
a,aa,aaa,aaaa,aaab,aaac,aaad,aaae,aaaf,aaag,aaah,aaai,aaaj,aaak,aaal,aaam
>>> print ','.join(nth_combination(n, 4, alphabet)
... for n in range(0, 14000000, 10**6))
a,emiL,iyro,mKz2,qWIF,u8Ri,zk0U,Dxav,HJi9,LVrM,P7Ap,UjJ1,YvSE,2H1h
"""
if maxlen == 1:
return alphabet[n]
offset, next_n = divmod(n, 1 + len(alphabet)**(maxlen-1))
if next_n == 0:
return alphabet[offset]
return alphabet[offset] + nth_combination(next_n-1, maxlen-1, alphabet)
if __name__ == '__main__':
from doctest import testmod
testmod()
Bien sûr, cela n'a de sens que si vous avez besoin d'un accès aléatoire à l'ensemble des combinaisons au lieu de itérer toujours par tous.
Si est élevé maxlen
, certains pourraient être atteint optimisation de la vitesse par exemple en se débarrassant de concaténation de chaînes et de re-calcul de la longueur de et alphabet
à chaque niveau maxlen-1
de la récursion. Une approche non récurrente pourrait donner un sens aussi.