web-dev-qa-db-ja.com

Python:ネストされた「for」ループ

すべてのn桁の数字を調べて、数字の2桁目が常に1桁目以下、3桁目が2桁目以下になるようにします。これは、次のような恐ろしいコードを書くことで取得できます。

for i in range(10):
    for j in range(i+1):
        for k in range(j+1):

など。しかし、10桁の数字で私のコードは恐ろしく見え始めます。また、それは多くの記述であり、それらのいくつかを賞賛したい場合、インデントは恐ろしくなります。これを取得するニースの簡潔な方法はありますか?

編集:ちょうど私がこれを気にしている理由を人々が知っているように、 https://projecteuler.net/problem=74 私は1から1ミリオンまでの数字をチェックします。残念ながら、それは思ったほど簡単ではありません。先頭にゼロのある数字は、内部にゼロのある数字とは異なる方法で処理されるため、追加の魔法を実行する必要がありました。とにかく、すべての洞察に満ちた提案に感謝します。

16
098799

itertoolsを使用できます:

>>> for comb in itertools.combinations_with_replacement(range(9, -1, -1), 3):
        print comb

(9, 9, 9)
(9, 9, 8)
(9, 9, 7)
(9, 9, 6)
...
(4, 0, 0)
(3, 3, 3)
(3, 3, 2)
(3, 3, 1)
(3, 3, 0)
(3, 2, 2)
(3, 2, 1)
(3, 2, 0)
(3, 1, 1)
(3, 1, 0)
(3, 0, 0)
(2, 2, 2)
(2, 2, 1)
(2, 2, 0)
(2, 1, 1)
(2, 1, 0)
(2, 0, 0)
(1, 1, 1)
(1, 1, 0)
(1, 0, 0)
(0, 0, 0)

または再帰的に、十分になるまでさらに数字を追加します。これにより、数字のタプルの代わりにintオブジェクトがより直接的に生成されます(実際に必要かどうかはわかりません)。

def build(enough, prefix=0):
    if prefix >= enough:
        print(prefix)
        return
    for digit in range(prefix % 10 + 1) if prefix else range(1, 10):
        build(enough, prefix * 10 + digit)

デモ(「000 "、とにかくそれが欲しいかどうかわからない):

>>> n = 3
>>> build(10**(n-1))
100
110
111
200
210
211
220
221
222
300
310
311
320
321
322
330
331
332
333
400
410
411
420
21
Stefan Pochmann

これは itertools を使用したアプローチです:

from itertools import combinations_with_replacement

N = 3

for kji in combinations_with_replacement((str(i) for i in range(10)), N):
    print(''.join(reversed(kji)))

順序は元のアプローチと同じではないことに注意してください。

私は最近 類似の質問 ...

4

単純な再帰的アプローチ:

def ordered_digits_generator(numDigits,min=1,max=9):
    for first in range(min,max+1):
        if numDigits == 1:
             yield first
        else:
             addend = first*10**(numDigits-1)
             for rest in ordered_digits(numDigits-1,min=0,max=first):
                 yield addend+rest

次に経由で呼び出されます:

for number in ordered_digits_generator(10):
    print number

期待どおりに動作します。

数学者のアプローチ

Itertoolsパッケージには、すでにこの再帰を本質的に実装するロジックが既にあります。おそらく私たちができるよりも優れており、重要なテストが行​​われています。したがって、次のように使用できます。

import itertools
def ordered_digits_combo(numDigits):
    exponent = [10**i for i in range(0,numDigits)]

    for subset in itertools.combinations(range(0,numDigits+9),numDigits):
        if subset[numDigits-1]>numDigits-1:
            v = 0
            for i in range(0,numDigits):
                v += exponent[i]*(subset[i]-i)
            yield v

a[0]<a[1]<...<a[n-1]{0,1,...,n+8}の順序付けられたサブセットが与えられると、iで番号を選択します番目 a[i]-iに等しい右からの数字。ケースa[n-1]==n-1を除外する必要があります。これは、すべてゼロの数字で構成されているためです。

3
Thomas Andrews

私はおそらくこれを再帰的に実装します:

def generate(max, digits):
    for d in range(max + 1):
        if digits == 1:
            yield d
        else:
            first = d * 10**(digits-1)
            for n in generate(d, digits - 1):
                yield first + n

出力:

In : list(generate(3, 3))
Out:
[0,
 100,
 110,
 111,
 200,
 210,
 211,
 220,
 221,
 222,
 300,
 310,
 311,
 320,
 321,
 322,
 330,
 331,
 332,
 333]
0
Brandon Mintern

最初にコメントしたように、@ iFloの提案を実装しました。それは非常に効率的ではありませんが、確かに年齢はかかりません。

def digit_test(n):
    while n > 9:
        if (n % 100 / 10) < (n % 10): return False
        n /= 10
    return True

# under a second to construct a list of all numbers below 1000000 meeting the criteria
candidates = [x for x in xrange(1,1000000) if digit_test(x)]

# should be 8001 elements, consistent with other algorithms
print len(candidates)
0
brian_o