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.
1 Answer 1
-
if key4 != key1 and key4 != key2 and key4 != key3:
If you append to
key_order
upon picking a non-duplicate value you can just usein
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
- You can just use
random.sample
orrandom.shuffle
and follow Python's "batteries included" ethos.
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.pop
ing 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]
-
\$\begingroup\$ This was incredibly informative (and practical). I greatly appreciate your feedback. \$\endgroup\$mellocellofello– mellocellofello2021年07月28日 00:29:46 +00:00Commented Jul 28, 2021 at 0:29