我尝试使用random.randint(0, 100)
,但有些数字是一样的。是否有方法/模块来创建列表唯一的随机数?
def getScores():
# open files to read and write
f1 = open("page.txt", "r");
p1 = open("pgRes.txt", "a");
gScores = [];
bScores = [];
yScores = [];
# run 50 tests of 40 random queries to implement "bootstrapping" method
for i in range(50):
# get 40 random queries from the 50
lines = random.sample(f1.readlines(), 40);
这将返回从0到99范围内选择的10个数字的列表,没有重复。
import random
random.sample(range(100), 10)
参考您的特定代码示例,您可能希望从文件中读取所有行一次,然后从内存中保存的列表中选择随机行。例如:
all_lines = f1.readlines()
for i in range(50):
lines = random.sample(all_lines, 40)
这样,您只需要在循环之前实际读取一次文件。这样做要比回寻文件的开头并为每次循环迭代再次调用f1.readlines()
要高效得多。
您可以使用Numpy库快速回答,如下所示 -
给定代码段列出了介于0到5之间的6个唯一数字。您可以调整参数以获得舒适感。
import numpy as np
import random
a = np.linspace( 0, 5, 6 )
random.shuffle(a)
print(a)
产量
[ 2. 1. 5. 3. 4. 0.]
它没有像我们在random.sample中看到的任何约束那样引用here。
希望这个对你有帮助。
一个非常简单的功能,也可以解决您的问题
from random import randint
data = []
def unique_rand(inicial, limit, total):
data = []
i = 0
while i < total:
number = randint(inicial, limit)
if number not in data:
data.append(number)
i += 1
return data
data = unique_rand(1, 60, 6)
print(data)
"""
prints something like
[34, 45, 2, 36, 25, 32]
"""
答案提供here在时间和内存方面非常有效,但由于它使用了高级python结构,例如yield,因此有点复杂。 simpler answer在实践中运行良好但是,答案的问题是它可能在实际构建所需的集合之前生成许多虚假整数。尝试使用populationSize = 1000,sampleSize = 999.理论上,它有可能不会终止。
下面的答案解决了这两个问题,因为它具有确定性和有效性,但目前效率不如其他两个。
def randomSample(populationSize, sampleSize):
populationStr = str(populationSize)
dTree, samples = {}, []
for i in range(sampleSize):
val, dTree = getElem(populationStr, dTree, '')
samples.append(int(val))
return samples, dTree
其中函数getElem,percolateUp如下所定义
import random
def getElem(populationStr, dTree, key):
msd = int(populationStr[0])
if not key in dTree.keys():
dTree[key] = range(msd + 1)
idx = random.randint(0, len(dTree[key]) - 1)
key = key + str(dTree[key][idx])
if len(populationStr) == 1:
dTree[key[:-1]].pop(idx)
return key, (percolateUp(dTree, key[:-1]))
newPopulation = populationStr[1:]
if int(key[-1]) != msd:
newPopulation = str(10**(len(newPopulation)) - 1)
return getElem(newPopulation, dTree, key)
def percolateUp(dTree, key):
while (dTree[key] == []):
dTree[key[:-1]].remove( int(key[-1]) )
key = key[:-1]
return dTree
最后,对于大的n值,平均时间约为15ms,如下所示,
In [3]: n = 10000000000000000000000000000000
In [4]: %time l,t = randomSample(n, 5)
Wall time: 15 ms
In [5]: l
Out[5]:
[10000000000000000000000000000000L,
5731058186417515132221063394952L,
85813091721736310254927217189L,
6349042316505875821781301073204L,
2356846126709988590164624736328L]
基于集合的方法(“如果返回值中的随机值,再试一次”)的问题在于它们的运行时由于冲突而未确定(这需要另一次“再试一次”迭代),尤其是当返回大量随机值时从范围。
不容易出现此非确定性运行时的替代方法如下:
import bisect
import random
def fast_sample(low, high, num):
""" Samples :param num: integer numbers in range of
[:param low:, :param high:) without replacement
by maintaining a list of ranges of values that
are permitted.
This list of ranges is used to map a random number
of a contiguous a range (`r_n`) to a permissible
number `r` (from `ranges`).
"""
ranges = [high]
high_ = high - 1
while len(ranges) - 1 < num:
# generate a random number from an ever decreasing
# contiguous range (which we'll map to the true
# random number).
# consider an example with low=0, high=10,
# part way through this loop with:
#
# ranges = [0, 2, 3, 7, 9, 10]
#
# r_n :-> r
# 0 :-> 1
# 1 :-> 4
# 2 :-> 5
# 3 :-> 6
# 4 :-> 8
r_n = random.randint(low, high_)
range_index = bisect.bisect_left(ranges, r_n)
r = r_n + range_index
for i in xrange(range_index, len(ranges)):
if ranges[i] <= r:
# as many "gaps" we iterate over, as much
# is the true random value (`r`) shifted.
r = r_n + i + 1
elif ranges[i] > r_n:
break
# mark `r` as another "gap" of the original
# [low, high) range.
ranges.insert(i, r)
# Fewer values possible.
high_ -= 1
# `ranges` happens to contain the result.
return ranges[:-1]
import random
result=[]
for i in range(1,50):
rng=random.randint(1,20)
result.append(rng)
从win xp中的CLI:
python -c "import random; print(sorted(set([random.randint(6,49) for i in range(7)]))[:6])"
在加拿大,我们有6/49乐透。我只是将上面的代码包装在lotto.bat中并运行C:\home\lotto.bat
或者只运行C:\home\lotto
。
因为random.randint
经常重复一个数字,我使用set
和range(7)
,然后将它缩短到6。
有时,如果一个数字重复超过2次,则结果列表长度将小于6。
编辑:但是,random.sample(range(6,49),6)
是正确的方法。
您可以使用random模块中的shuffle函数,如下所示:
import random
my_list = list(xrange(1,100)) # list of integers from 1 to 99
# adjust this boundaries to fit your needs
random.shuffle(my_list)
print my_list # <- List of unique random numbers
请注意,shuffle方法不会像人们预期的那样返回任何列表,它只会随机引用传递的列表。
您可以先创建一个从a
到b
的数字列表,其中a
和b
分别是列表中最小和最大的数字,然后使用Fisher-Yates算法或使用Python的random.shuffle
方法对其进行混洗。
在this answer中提出的解决方案有效,但如果样本量很小,则可能会成为记忆问题,但人口庞大(例如random.sample(insanelyLargeNumber, 10)
)。
要解决这个问题,我会这样做:
answer = set()
sampleSize = 10
answerSize = 0
while answerSize < sampleSize:
r = random.randint(0,100)
if r not in answer:
answerSize += 1
answer.add(r)
# answer now contains 10 unique, random integers from 0.. 100
所以,我意识到这篇文章已有6年历史了,但是(通常)更好的算法性能还有另一个答案,尽管实用性较低,开销较大。
其他答案包括shuffle方法和使用集合的'try until valid'方法。
如果我们从区间0 ... N-1中随机选择K个整数而没有替换,那么shuffle方法使用O(N)存储和O(N)操作,如果我们从大N中选择小K,这很烦人。set方法仅使用O(K)存储,但最坏情况O(inf)预期O(n * log(n))K接近N.(想象一下,尝试随机获取两个允许答案中的最后一个数字,已经选择999998,因为k = n-1 = 10 ^ 6)。
所以set方法适用于K~1,而shuffle方法适用于K~N。两者都使用预期的> K RNG呼叫。
其他方式;你可以假装做Fisher-Yates shuffle,并且对于每个新的随机选择,对你已经选择的元素执行二进制搜索操作,以找到你实际存储所有元素的数组时你将获得的值还没选好。
如果您已经选择的值是[2,4],并且您的随机数生成器在间隔(N-num_already_selected)中吐出2,那么您假装选择[0,1,3,5,6,... 。]通过计算小于已经选择的答案的值。在这种情况下,您的第三个选定值将是3.然后,在下一步中,如果您的随机数再次为2,它将映射到5(在假装列表[0,1,5,6]中),因为(已经选择的值[2,3,4]的排序列表中的潜在索引为5,其为3)+ 2 = 5。
因此,将已选择的值存储在平衡二叉搜索树中,在每个节点存储秩(小于该值的值),从范围中选择一个随机数R(0 ... n-(已选择的数字) )。然后像搜索一样下降树,但是你的搜索值是R加上你所在节点的等级。到达叶节点时,将随机数添加到该节点的等级,并将总和插入到平衡二叉树中。
一旦你有了K个元素,就把它们从树上读到一个数组并随机播放(如果顺序很重要)。
这需要O(K)存储,O(K * log(K))性能以及K randint调用。
随机抽样的示例实现(非随机最终排序,但你可以在O(K)后洗牌),O(k)存储和O(klog ^ 2(k))性能(不是O(klog(k)),因为我们可以为此实现自定义下降平衡二叉树树:
from sortedcontainers import SortedList
def sample(n, k):
'''
Return random k-length-subset of integers from 0 to n-1. Uses only O(k)
storage. Bounded k*log^2(k) worst case. K RNG calls.
'''
ret = SortedList()
for i in range(k):
to_insert = random.randint(0, n-1 - len(ret))
to_insert = binsearch_adding_rank(ret, to_insert)
ret.add(to_insert)
return ret
def binsearch_adding_rank(A, v):
l, u = 0, len(A)-1
m=0
while l <= u:
m = l+(u-l)//2
if v + m >= A[m]:
l = m+1
m+=1 # We're binary searching for partitions, so if the last step was to the right then add one to account for offset because that's where our insert would be.
elif v+m < A[m]:
u = m-1
return v+m
并显示有效性:
如果我们正在进行渔民洗牌,已经选择了[1,4,6,7,8,9,15,16],随机数为5,我们尚未选择的数组看起来像[0 ,2,3,5,10,11,12,...],所以元素5是11.因此我们的binsearch函数应该返回11,给定5和[1,4,6,7,8,9,15 ,16]:
assert binsearch_adding_rank([1,4,6,7,8,9,15,16], 5) == 11
[1,2,3]的反函数是[0,4,5,6,7,8,...],其中第5个元素是8,所以:
assert binsearch_adding_rank([1,2,3], 5) == 8
[2,3,5]的逆是[0,1,4,6,...],其第一个元素是(仍)1,所以:
assert binsearch_adding_rank([2,3,5], 1) == 1
反向是[0,6,7,8,...],第三个元素是8,并且:
assert binsearch_adding_rank([1,2,3,4,5,10], 3) == 8
并测试整体功能:
# Edge cases:
assert sample(50, 0) == []
assert sample(50, 50) == list(range(0,50))
# Variance should be small and equal among possible values:
x = [0]*10
for i in range(10_000):
for v in sample(10, 5):
x[v] += 1
for v in x:
assert abs(5_000 - v) < 250, v
del x
# Check for duplication:
y = sample(1500, 1000)
assert len(frozenset(y)) == len(y)
del y
但实际上,对K~> N / 2使用shuffle方法,对K~ <N / 2使用set方法。
编辑:这是使用递归的另一种方式! O(k * log(n))我想。
def divide_and_conquer_sample(n, k, l=0):
u = n-1
# Base cases:
if k == 0:
return []
elif k == n-l:
return list(range(l, n))
elif k == 1:
return [random.randint(l, u)]
# Compute how many left and how many right:
m = l + (u-l)//2
k_right = 0
k_left = 0
for i in range(k):
# Base probability: (# of available values in right interval) / (total available values)
if random.random() <= (n-m - k_right)/(n-l-k_right-k_left):
k_right += 1
else:
k_left += 1
# Recur
return divide_and_conquer_sample(n, k_right, m) + divide_and_conquer_sample(m, k_left, l)
如果您需要采样非常大的数字,则不能使用range
random.sample(range(10000000000000000000000000000000), 10)
因为它抛出:
OverflowError: Python int too large to convert to C ssize_t
此外,如果由于范围太小,random.sample
无法生成您想要的项目数
random.sample(range(2), 1000)
它抛出:
ValueError: Sample larger than population
此功能解决了这两个问题:
import random
def random_sample(count, start, stop, step=1):
def gen_random():
while True:
yield random.randrange(start, stop, step)
def gen_n_unique(source, n):
seen = set()
seenadd = seen.add
for i in (i for i in source() if i not in seen and not seenadd(i)):
yield i
if len(seen) == n:
break
return [i for i in gen_n_unique(gen_random,
min(count, int(abs(stop - start) / abs(step))))]
使用非常大的数字:
print('\n'.join(map(str, random_sample(10, 2, 10000000000000000000000000000000))))
样本结果:
7822019936001013053229712669368
6289033704329783896566642145909
2473484300603494430244265004275
5842266362922067540967510912174
6775107889200427514968714189847
9674137095837778645652621150351
9969632214348349234653730196586
1397846105816635294077965449171
3911263633583030536971422042360
9864578596169364050929858013943
范围小于请求项数的用法:
print(', '.join(map(str, random_sample(100000, 0, 3))))
样本结果:
2, 0, 1
它也适用于负范围和步骤:
print(', '.join(map(str, random_sample(10, 10, -10, -2))))
print(', '.join(map(str, random_sample(10, 5, -5, -2))))
样品结果:
2, -8, 6, -2, -4, 0, 4, 10, -6, 8
-3, 1, 5, -1, 3
如果随机生成从1到N的N个数字的列表,则是,可能会重复某些数字。
如果你想要一个随机顺序从1到N的数字列表,请填充一个从1到N的整数的数组,然后使用Fisher-Yates shuffle或Python的random.shuffle()
。
O(1)记忆
或(k)行动
用简单的Linear Congruential Generator可以解决这个问题。这需要恒定的存储器开销(8个整数)和最多2 *(序列长度)计算。
所有其他解决方案使用更多内存和更多计算!如果您只需要一些随机序列,这种方法会明显更便宜。对于大小N
的范围,如果你想生成N
唯一的k
序列或更多的顺序,我建议使用内置方法random.sample(range(N),k)
作为这个has been optimized在python中的速度接受的解决方案。
# Return a randomized "range" using a Linear Congruential Generator
# to produce the number sequence. Parameters are the same as for
# python builtin "range".
# Memory -- storage for 8 integers, regardless of parameters.
# Compute -- at most 2*"maximum" steps required to generate sequence.
#
def random_range(start, stop=None, step=None):
import random, math
# Set a default values the same way "range" does.
if (stop == None): start, stop = 0, start
if (step == None): step = 1
# Use a mapping to convert a standard range into the desired range.
mapping = lambda i: (i*step) + start
# Compute the number of numbers in this range.
maximum = (stop - start) // step
# Seed range with a random integer.
value = random.randint(0,maximum)
#
# Construct an offset, multiplier, and modulus for a linear
# congruential generator. These generators are cyclic and
# non-repeating when they maintain the properties:
#
# 1) "modulus" and "offset" are relatively prime.
# 2) ["multiplier" - 1] is divisible by all prime factors of "modulus".
# 3) ["multiplier" - 1] is divisible by 4 if "modulus" is divisible by 4.
#
offset = random.randint(0,maximum) * 2 + 1 # Pick a random odd-valued offset.
multiplier = 4*(maximum//4) + 1 # Pick a multiplier 1 greater than a multiple of 4.
modulus = int(2**math.ceil(math.log2(maximum))) # Pick a modulus just big enough to generate all numbers (power of 2).
# Track how many random numbers have been returned.
found = 0
while found < maximum:
# If this is a valid value, yield it in generator fashion.
if value < maximum:
found += 1
yield mapping(value)
# Calculate the next value in the sequence.
value = (value*multiplier + offset) % modulus
该函数“random_range”的使用与任何生成器(如“range”)的使用相同。一个例子:
# Show off random range.
print()
for v in range(3,6):
v = 2**v
l = list(random_range(v))
print("Need",v,"found",len(set(l)),"(min,max)",(min(l),max(l)))
print("",l)
print()
Required 8 cycles to generate a sequence of 8 values.
Need 8 found 8 (min,max) (0, 7)
[1, 0, 7, 6, 5, 4, 3, 2]
Required 16 cycles to generate a sequence of 9 values.
Need 9 found 9 (min,max) (0, 8)
[3, 5, 8, 7, 2, 6, 0, 1, 4]
Required 16 cycles to generate a sequence of 16 values.
Need 16 found 16 (min,max) (0, 15)
[5, 14, 11, 8, 3, 2, 13, 1, 0, 6, 9, 4, 7, 12, 10, 15]
Required 32 cycles to generate a sequence of 17 values.
Need 17 found 17 (min,max) (0, 16)
[12, 6, 16, 15, 10, 3, 14, 5, 11, 13, 0, 1, 4, 8, 7, 2, ...]
Required 32 cycles to generate a sequence of 32 values.
Need 32 found 32 (min,max) (0, 31)
[19, 15, 1, 6, 10, 7, 0, 28, 23, 24, 31, 17, 22, 20, 9, ...]
Required 64 cycles to generate a sequence of 33 values.
Need 33 found 33 (min,max) (0, 32)
[11, 13, 0, 8, 2, 9, 27, 6, 29, 16, 15, 10, 3, 14, 5, 24, ...]