Python random unique values from two non overlapping ranges
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------
Music by Eric Matyas
https://www.soundimage.org
Track title: Magical Minnie Puzzles
--
Chapters
00:00 Python Random Unique Values From Two Non Overlapping Ranges
01:04 Answer 1 Score 1
01:23 Answer 2 Score 1
01:35 Accepted Answer Score 3
02:18 Answer 4 Score 1
02:58 Thank you
--
Full question
https://stackoverflow.com/questions/3025...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #random
#avk47
ACCEPTED ANSWER
Score 3
randlist = [r + (end - start + 1) * (r >= start) for r in
random.sample(range(total - end + start), 100)]
Example / "proof":
- total=10, start=2, end=5
- There are 7 allowed numbers: 0, 1, 6, 7, 8, 9, 10
- range(total-end+start) = range(7) picks from 7 numbers 0..6 (so far so good)
- Numbers larger than or equal to start=2 are shifted upwards by end-start+1=4
- Resulting numbers are in 0, 1, 6, 7, 8, 9, 10.
Demo:
>>> sorted(randlist2(2000000000, 10000000, 1900000000))
[176827, 3235435, 3278133, 3673989, 5148447, 8314140, 8885997, 1900189345, 1902880599,
...
1997494057, 1997538971, 1997854443, 1997907285]
This works until over 2 billion, easily beating the required upper limit of "the number of wikipedia english wikipedia pages, so whatever many million that is" :-). After that it gets OverflowError: Python int too large to convert to C ssize_t. I see no spike in memory usage of my PC and the result is instant. This is using Python 3, obviously.
ANSWER 2
Score 1
Try the following function:
def rand_choice(start, end, amount, istart, iend):
from random import randint
all = []
for i in range(amount):
randnum = istart
while randnum in range(istart, iend+1) or randnum in all:
randnum = randint(start, end)
all.append(randnum)
return all
>>> rand_choice(1, 1000, 10, 10, 20)
[30, 798, 427, 229, 943, 386, 749, 925, 520, 877]
>>> rand_choice(1, 1000, 10, 10, 20)
[414, 351, 898, 813, 91, 205, 751, 269, 360, 501]
>>>
ANSWER 3
Score 1
Just a slight variation of the original:
def randlist(total, start, end):
import random
randset = set()
while len(randset) < 100:
temp = random.randint(0, total)
start <= temp <= end or randset.add(temp)
return random.sample(randset, 100)
ANSWER 4
Score 1
Another answerer earlier had a pretty cool idea of concatenating ranges together into a single Sequence class. The code had some issues, but I managed to make a version that seems to work with random.sample.
import collections, random
class range_duo(collections.Sequence):
def __init__(self, r1, r2):
self.r1 = r1
self.r2 = r2
self.l1 = len(r1)
self.l2 = len(r2)
self.total_length = len(r1) + len(r2)
def __len__(self):
return self.total_length
def __getitem__(self, key):
if key < self.l1:
return self.r1[key]
else:
return self.r2[key-self.l1]
# Solving the example in the original question:
rd = range_duo(range(0, 10), range(20, 10000))
sample = random.sample(rd, 100)
print(sample)
Obviously this class isn't perfect, but my only goal was to solve the problem with random.sample using a minimum memory footprint. In Python 2.x, xrange should be used instead of range.