Python: nested 'for' loops

前端 未结 5 1198
天涯浪人
天涯浪人 2021-02-07 09:47

I\'d like to go through all n-digit numbers such that second digit of the number is always lower or equal to the first, third is lower or equal to the second etc. I can get this

相关标签:
5条回答
  • 2021-02-07 09:59

    I would probably implement this recursively:

    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
    

    The output:

    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 讨论(0)
  • 2021-02-07 10:06

    A simple recursive approach:

    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
    

    Then called via:

    for number in ordered_digits_generator(10):
        print number
    

    works as expected.

    The mathematician's approach

    The itertools package already has logic which essentially already implements this recursion. Presumably better than we can, with significant testing. So we can use it as follows:

    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
    

    Given an ordered subset a[0]<a[1]<...<a[n-1] of {0,1,...,n+8}, we pick the number with the ith digit from the right equal to a[i]-i. We have to exclude the case a[n-1]==n-1 because that consists of he number with all zeros.

    0 讨论(0)
  • 2021-02-07 10:07

    I implemented @iFlo's suggestion as commented originally. It's not hyper efficient but it certainly doesn't take ages.

    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 讨论(0)
  • 2021-02-07 10:16

    Could use 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)
    

    Or recursively, appending more and more digits until enough, which can more directly produce int objects instead of digit tuples (not sure whether that's what you actually need):

    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)
    

    Demo (note it leaves out "000", not sure whether you'd want that anyway):

    >>> 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
    
    0 讨论(0)
  • 2021-02-07 10:20

    this an approach using 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)))
    

    note that the order is not the same as in your original approach.

    i recently had a simliar question...

    0 讨论(0)
提交回复
热议问题