Python 来自两个不重叠范围的随机唯一值
Python random unique values from two non overlapping ranges
正如标题所说的,来自两个范围的 100 个随机唯一值,或者更准确地说,有一个范围和一个子范围被排除在有效值之外。
示例的范围是 0 到 10000,随机 100 个不在 10 到 20 范围内的数字
要求:
- 子范围可以在最开始或最后。
- 内存开销降至绝对最小值。
- 尽可能接近 random.shuffle() 的随机化。
我知道 random.sample(xrange(0,10000),100) 给出 100 个唯一值。
设置我会存储三个值 [start,end,total]
- start = 子范围的开始
- end = 子范围结束
- 总计 = 范围的长度
我能想到的最好的:
randlist=[]
while len(randlist)<100:
temp=random.randint(0,total)
if temp < start or temp > end:
if temp not in randlist:
randlist.append(temp)
这是真随机(伪随机)还是我以任何方式影响它?
尝试以下功能:
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]
>>>
与原版稍有不同:
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)
randlist = [r + (end - start + 1) * (r >= start) for r in
random.sample(range(total - end + start), 100)]
例子/"proof":
- 总=10,开始=2,结束=5
- 有 7 个允许的数字:0、1、6、7、8、9、10
- range(total-end+start) = range(7) 从 7 个数字 0..6 中挑选(到目前为止还不错)
- 大于等于start=2的数向上移动end-start+1=4
- 结果数字为 0、1、6、7、8、9、10。
演示:
>>> sorted(randlist2(2000000000, 10000000, 1900000000))
[176827, 3235435, 3278133, 3673989, 5148447, 8314140, 8885997, 1900189345, 1902880599,
...
1997494057, 1997538971, 1997854443, 1997907285]
这一直有效到超过 20 亿,轻松超过所需的上限 "the number of wikipedia english wikipedia pages, so whatever many million that is" :-)。之后它得到 OverflowError: Python int too large to convert to C ssize_t
。我没有看到我的 PC 内存使用量出现峰值,结果是即时的。显然,这是使用 Python 3。
另一位回答者早些时候有一个很酷的想法,即将范围连接成一个 Sequence
class。代码有一些问题,但我设法制作了一个似乎适用于 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)
显然这个 class 并不完美,但我唯一的目标是使用最少的内存占用来解决 random.sample
的问题。在Python 2.x中,应该使用xrange
而不是range
。
正如标题所说的,来自两个范围的 100 个随机唯一值,或者更准确地说,有一个范围和一个子范围被排除在有效值之外。
示例的范围是 0 到 10000,随机 100 个不在 10 到 20 范围内的数字
要求:
- 子范围可以在最开始或最后。
- 内存开销降至绝对最小值。
- 尽可能接近 random.shuffle() 的随机化。
我知道 random.sample(xrange(0,10000),100) 给出 100 个唯一值。
设置我会存储三个值 [start,end,total]
- start = 子范围的开始
- end = 子范围结束
- 总计 = 范围的长度
我能想到的最好的:
randlist=[]
while len(randlist)<100:
temp=random.randint(0,total)
if temp < start or temp > end:
if temp not in randlist:
randlist.append(temp)
这是真随机(伪随机)还是我以任何方式影响它?
尝试以下功能:
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]
>>>
与原版稍有不同:
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)
randlist = [r + (end - start + 1) * (r >= start) for r in
random.sample(range(total - end + start), 100)]
例子/"proof":
- 总=10,开始=2,结束=5
- 有 7 个允许的数字:0、1、6、7、8、9、10
- range(total-end+start) = range(7) 从 7 个数字 0..6 中挑选(到目前为止还不错)
- 大于等于start=2的数向上移动end-start+1=4
- 结果数字为 0、1、6、7、8、9、10。
演示:
>>> sorted(randlist2(2000000000, 10000000, 1900000000))
[176827, 3235435, 3278133, 3673989, 5148447, 8314140, 8885997, 1900189345, 1902880599,
...
1997494057, 1997538971, 1997854443, 1997907285]
这一直有效到超过 20 亿,轻松超过所需的上限 "the number of wikipedia english wikipedia pages, so whatever many million that is" :-)。之后它得到 OverflowError: Python int too large to convert to C ssize_t
。我没有看到我的 PC 内存使用量出现峰值,结果是即时的。显然,这是使用 Python 3。
另一位回答者早些时候有一个很酷的想法,即将范围连接成一个 Sequence
class。代码有一些问题,但我设法制作了一个似乎适用于 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)
显然这个 class 并不完美,但我唯一的目标是使用最少的内存占用来解决 random.sample
的问题。在Python 2.x中,应该使用xrange
而不是range
。