1
\$\begingroup\$

I was thinking of the following problem: List all sequences \$ a_1, a_2, a_3, a_4, a_5, a_6, a_7 \$ of positive integers where \$ 1 \le a_1 < a_2 < \ldots < a_7 \le 39 \$. For any sequence with this property, my code computes the next sequence in lexicographic order. Starting with the initial sequence \$ [1,2,3,4,5,6,7] \$, calling next_row(a) repeatedly produces all such sequences.

Is there more elegant way to code the function that returns the next row than this?

def next_row(a):
    if a[-1]<39:
             a[-1]+=1
             return a
    elif a[-2]<38:
            a[-2]+=1
            a[-1]=a[-2]+1
            return a
    elif a[-3]<38:
            a[-3]+=1
            a[-2]=a[-3]+1
            a[-1]=a[-2]+1
            return a
    elif a[-4]<37:
            a[-4]+=1
            a[-3]=a[-4]+1
            a[-2]=a[-3]+1
            a[-1]=a[-2]+1
            return a
    elif a[-5]<36:
            a[-5]+=1
            a[-4]=a[-5]+1
            a[-3]=a[-4]+1
            a[-2]=a[-3]+1
            a[-1]=a[-2]+1
            return a
    elif a[-6]<35:
            a[-6]+=1
            a[-5]=a[-6]+1
            a[-4]=a[-5]+1
            a[-3]=a[-4]+1
            a[-2]=a[-3]+1
            a[-1]=a[-2]+1
            return a
    elif a[-7]<34:
            a[-7]+=1
            a[-6]=a[-7]+1
            a[-5]=a[-6]+1
            a[-4]=a[-5]+1
            a[-3]=a[-4]+1
            a[-2]=a[-3]+1
            a[-1]=a[-2]+1
            return a
\$\endgroup\$
5
  • 1
    \$\begingroup\$ I'm a little confused, what exactly does this code do? \$\endgroup\$
    – Parekh
    Nov 7 '20 at 20:30
  • \$\begingroup\$ You didn't mention in the question, but do the integers have to be consecutive? The implementation seems to imply that \$\endgroup\$ Nov 8 '20 at 5:17
  • \$\begingroup\$ I am trying to make a program that gives me random lotto rows and check if the rows for lotto covering design, if 7 numbers are chosen from 39 numbers and you win if you get 4 or more common numbers. Therefore I tried to list all possible rows. \$\endgroup\$
    – user232941
    Nov 8 '20 at 9:44
  • \$\begingroup\$ @user232941: I have taken the liberty to edit the task description a bit, trying to make it more clear. Please check if that still matches your intentions. \$\endgroup\$
    – Martin R
    Nov 10 '20 at 3:30
  • \$\begingroup\$ I'm still trying to understand what your code does, please also provide sample input/outputs :) \$\endgroup\$
    – Parekh
    Nov 10 '20 at 5:08
2
\$\begingroup\$

There is a lot of repetition in your code, which makes it unnecessarily long, difficult to read, difficult to maintain, and error-prone. And in fact you have an apparent copy-paste error, can you spot it?

if a[-1]<39:
    ...
elif a[-2]<38:
    ...
elif a[-3]<38:
    ...
elif a[-4]<37:
    ...

The outer if/elif chain is to find the rightmost position in the list which can be increased, and that can be done with a loop:

for i in range(len(a)):
    if a[-1-i] < 39 - i:
       ...

The inner repeated assignments can be replaced by a loop as well. Then your function would look like this:

def next_row(a):
    for i in range(len(a)):
        if a[-1-i] < 39 - i:
            a[-1-i] += 1
            for j in range(0, i):
                a[-1-j] = a[-1-i] + i - j
            return a

Here i and j are positions from the end of the array as in your code, other choices are possible.

The new function is shorter, more general (it works with lists of arbitrary lists, not only lists of length 7), and can easily be generalized to arbitrary upper bounds (just replace the constant 39 by a function parameter).

If the intention is to enumerate all 7-element subsets of the first 39 integers then you can also use existing functions from the itertools module, in this case itertools.combinations():

import itertools

rows = itertools.combinations(range(1, 40), 7)

This creates an iterator which you can access one-by-one:

next_row = next(rows)
print(next_row) 
# (1, 2, 3, 4, 5, 6, 7)

next_row = next(rows)
print(next_row)
# (1, 2, 3, 4, 5, 6, 8)

...

or enumerate:

for row in rows:
    print(row)

As a general remark, I suggest to check your code against the PEP8 coding style, for example at PEP8 online. It will report mainly missing whitespace around operators, and wrong indentation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.