ネストされた For ループを置き換えるかどうか
-
20-08-2019 - |
質問
一連の 4 文字 (またはそれ以下) の文字列をループするスクリプトがあります。例えば:
aaaa
aaab
aaac
aaad
次のようにネストされた for ループで実装できた場合:
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)
この種のループのネストは悪いことなのでしょうか?もしそうであれば、私がやっていることを達成するより良い方法は何でしょうか?
解決
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))
それはあなたが探しているすべての 15018570
の言葉を印刷します。あなたはより多く/少なく単語をしたい場合は、単にMAX_CHARS
変数を変更します。それはまだ文字の任意の数のちょうど2つのfor
秒を持って、そしてあなた自身を繰り返す必要はありません。そして、かなり読みやすいです。ます。
他のヒント
私が最も読みやすいと少なくともスケーラブルとして私の答えを提出するつもりです
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
編集:それは長さ<4のすべての文字列の重複を生産するよう実は、これは、間違っています。 chars
配列から空の文字列を削除すると、わずか4文字の文字列を生成します。
通常、私はこの回答を削除したいが、あなたは同じ長さの文字列を生成する必要がある場合、私はまだちょっとそれを好むます。
最初のプログラマのために書く - コンピュータ秒を
。
それはそれから理解することが明らかと明らかだ場合はその正しいます。
スピード事項およびコンパイラはとにかくそれを最適化していない場合は、それを測定し、問題であるならば - !そして速く賢い方法を考える。
それを理解する(そして文書化する)のであれば、それは悪いことではないと思います。もっと Python 的な方法や賢い解決策 (ラムダなどを使用したもの) があるかもしれないことは疑いありませんが、私は常に賢さよりも読みやすさを優先してきました。
1 文字、2 文字、3 文字、4 文字の「単語」の可能性をすべて生成する必要があるため、この方法は他の方法と同じくらい優れています。実質的に (非常に大まかに) 1,400 万行の出力を生成することになるため、どのくらいの時間がかかるかわかりません (ただし、おそらくどのソリューションにも同じ問題が発生するでしょう)。
共通プレフィックスを事前に計算すると速度が向上する可能性がありますが、それを測定して確認することをお勧めします (いつも チェック、 一度もない 仮定する):
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)
編集:実際にいくつかのベンチマークを実行しました (Windows-Python 2.6.1 を使用)。このバージョンでは、元の 2.84 と比較して約 2.25 時間単位かかり、26% 高速になっています。それはその使用を正当化するかもしれないと思います(繰り返しになりますが、それが何を達成しようとしているのかが明確に文書化されている限り)。
@noskloさん そして @トリプティク ソリューションではさまざまな結果が得られます。
>>> 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']
以下は、@nosklo のものと同じ出力を生成する、変更された @Triptych のソリューションです。
>>> 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))))
['', 'a', 'b', 'aa', 'ab', 'ba', 'bb', 'aaa', 'aab', 'aba', 'abb', 'baa', 'bab', 'bba', 'bbb']
セットのすべての順列を生成するための多くのアルゴリズムがあります。あなたがここに欲しいのは、関連する問題であるが、直接analagousません。 推奨読書する
これは正確に質問に答えていないが、これは使用するためにアルファベットに与えられた最大の長さと文字のn
番目の組み合わせを返します。
#!/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()
これはもちろん、あなたの代わりに、常にそれらすべてを反復処理の組み合わせのセットへのランダムアクセスが必要な場合にのみ意味を成します。
maxlen
が高い場合は、、いくつかの速度の最適化は、例えば達成することができました文字列の連結とを取り除くことにより、再帰の各レベルでalphabet
maxlen-1
の長さを再計算し。非再帰的なアプローチは、あまりにも、意味をなさないかもしれません。