6
\$\begingroup\$

My goal is to write code that picks several random numbers from a range without repeating any previously chosen numbers. I figured out a functional solution for my problem but it seems very bulky and there must be a way to do this in fewer lines. Mine feels brute force and not very elegant and becomes super unwieldy if stretched to larger ranges and repetitions. Would love any suggestions for cleaning up what I already have.

import random

key1 = random.randint(1, 4)
while True:
    key2 = random.randint(1, 4)
    if key2 != key1:
        break
while True:
    key3 = random.randint(1, 4)
    if key3 != key1 and key3 != key2:
        break
while True:
    key4 = random.randint(1, 4)
    if key4 != key1 and key4 != key2 and key4 != key3:
        break

key_order = [key1, key2, key3, key4]
print(key_order)

If it means totally taking my code apart and rebuilding it, that is fine. I am here to learn.

\$\endgroup\$
5
\$\begingroup\$
  • if key4 != key1 and key4 != key2 and key4 != key3:

    If you append to key_order upon picking a non-duplicate value you can just use in instead.

  • You can then more simply use a for loop to select the number of keys to output.

key_order = []
for _ in range(4):
    while True:
        key = random.randint(1, 4)
        if key not in key_order:
            key_order.append(key)
            break

key_order = random.sample(range(1, 5), 4)
key_order = random.shuffle(range(1, 5))

Your algorithm is known as the naive shuffle, because the algorithm is very simple but also very slow. If you want to implement the shuffle yourself there's a very clear visual explanation on how the Fisher-Yates shuffle works.

Whatever you do you should remove the while loop by only picking a random number from the values you have left. You can do so by building a list of options to pick from by list.poping when you key_order.append(key).

Whilst pop is less expensive then your while loop when shuffling, pop is still expensive. So we can change append and pop to a swap operation. For the first selection we swap 0 with random.randrange(0, 4), then for the second selection 1 with random.randrange(1, 4)... Getting the Fisher-Yates shuffle.

values = [0, 1, 2, 3]
for i in range(len(values)):
    j = random.randrange(i, len(values))
    values[i], values[j] = values[j], values[i]
\$\endgroup\$
1
  • \$\begingroup\$ This was incredibly informative (and practical). I greatly appreciate your feedback. \$\endgroup\$ Jul 28 at 0:29

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.