如何在 Python 中生成唯一随机浮点数列表
How to generate list of unique random floats in Python
我知道有一些简单的方法可以生成唯一随机整数列表(例如 random.sample(range(1, 100), 10)
)。
我想知道是否有更好的方法来生成唯一随机浮点数列表,除了编写一个函数,它的作用类似于一个范围,但接受这样的浮点数:
import random
def float_range(start, stop, step):
vals = []
i = 0
current_val = start
while current_val < stop:
vals.append(current_val)
i += 1
current_val = start + i * step
return vals
unique_floats = random.sample(float_range(0, 2, 0.2), 3)
有更好的方法吗?
回答
一个简单的方法是保留一组到目前为止看到的所有随机值,如果有重复则重新选择:
import random
def sample_floats(low, high, k=1):
""" Return a k-length list of unique random floats
in the range of low <= x <= high
"""
result = []
seen = set()
for i in range(k):
x = random.uniform(low, high)
while x in seen:
x = random.uniform(low, high)
seen.add(x)
result.append(x)
return result
备注
这项技术是 Python 自己的 random.sample() 的实现方式。
该函数使用 set 来跟踪之前的选择,因为搜索集合的复杂度为 O(1),而搜索列表的复杂度为 O(n)。
计算重复选择的概率等同于著名的Birthday Problem。
给定来自 random() 的 2**53 个不同的可能值,重复项很少见。
平均而言,重复浮点数约为 120,000,000 个样本。
变体:有限的浮动范围
如果人口仅限于一系列均匀分布的浮点数,则可以使用 random.sample() directly. The only requirement is that the population be a Sequence:
from __future__ import division
from collections import Sequence
class FRange(Sequence):
""" Lazily evaluated floating point range of evenly spaced floats
(inclusive at both ends)
>>> list(FRange(low=10, high=20, num_points=5))
[10.0, 12.5, 15.0, 17.5, 20.0]
"""
def __init__(self, low, high, num_points):
self.low = low
self.high = high
self.num_points = num_points
def __len__(self):
return self.num_points
def __getitem__(self, index):
if index < 0:
index += len(self)
if index < 0 or index >= len(self):
raise IndexError('Out of range')
p = index / (self.num_points - 1)
return self.low * (1.0 - p) + self.high * p
这里是从 10.0 到 20.0 的 41 个均匀间隔的浮点数范围内选择十个随机样本而不放回的示例。
>>> import random
>>> random.sample(FRange(low=10.0, high=20.0, num_points=41), k=10)
[13.25, 12.0, 15.25, 18.5, 19.75, 12.25, 15.75, 18.75, 13.0, 17.75]
您可以轻松地使用整数列表生成浮点数:
int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
查看this Stack Overflow question关于生成随机浮点数的信息。
如果您希望它与 python2 一起使用,请添加此导入:
from __future__ import division
您可以直接使用 random.uniform(start, stop)
。使用双精度浮点数,如果您的集合很小,您可以相对确定它们是唯一的。如果您想生成大量随机浮点数并且需要避免您有一个数字两次,请在将它们添加到列表之前进行检查。
但是,如果您正在寻找特定数字的选择,这不是解决方案。
min_val=-5
max_val=15
numpy.random.random_sample(15)*(max_val-min_val) + min_val
或使用制服
numpy.random.uniform(min_val,max_val,size=15)
如文档中所述 Python 具有 random.random() 函数:
import random
random.random()
然后你会得到一个浮点值:0.672807098390448
所以你需要做的就是做一个for
循环并打印出random.random():
>>> for i in range(10):
print(random.random())
如果需要保证唯一性,可能效率更高
- 尝试立即在
[lo, hi]
中生成 n
个随机浮点数。
- 如果唯一浮点数的长度不是
n
,请尝试生成,但是仍然需要很多浮点数
并相应地继续直到你有足够的,而不是在 Python 级别循环检查集合中逐一生成它们。
如果您负担得起 NumPy,那么使用 np.random.uniform
可以大大加快速度。
import numpy as np
def gen_uniq_floats(lo, hi, n):
out = np.empty(n)
needed = n
while needed != 0:
arr = np.random.uniform(lo, hi, needed)
uniqs = np.setdiff1d(np.unique(arr), out[:n-needed])
out[n-needed: n-needed+uniqs.size] = uniqs
needed -= uniqs.size
np.random.shuffle(out)
return out.tolist()
如果您不能使用 NumPy,它仍然可能更有效,具体取决于您的数据需要应用相同的概念,即事后检查重复项,维护一个集合。
def no_depend_gen_uniq_floats(lo, hi, n):
seen = set()
needed = n
while needed != 0:
uniqs = {random.uniform(lo, hi) for _ in range(needed)}
seen.update(uniqs)
needed -= len(uniqs)
return list(seen)
粗略基准
极端退化案例
# Mitch's NumPy solution
%timeit gen_uniq_floats(0, 2**-50, 1000)
<b>153 µs ± 3.71 µs per loop</b> (mean ± std. dev. of 7 runs, 10000 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 2**-50, 1000)
<b>495 µs ± 43.9 µs per loop</b> (mean ± std. dev. of 7 runs, 1000 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 2**-50, 1000)
<b>618 µs ± 13 µs per loop</b> (mean ± std. dev. of 7 runs, 1000 loops each)
更多"normal"案例(样本较大)
# Mitch's NumPy solution
%timeit gen_uniq_floats(0, 1, 10**5)
<b>15.6 ms ± 1.12 ms per loop</b> (mean ± std. dev. of 7 runs, 100 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 1, 10**5)
<b>65.7 ms ± 2.31 ms per loop</b> (mean ± std. dev. of 7 runs, 10 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 1, 10**5)
<b>78.8 ms ± 4.22 ms per loop</b> (mean ± std. dev. of 7 runs, 10 loops each)
more_itertools
has a generic numeric_range
处理整数和浮点数。
import random
import more_itertools as mit
random.sample(list(mit.numeric_range(0, 2, 0.2)), 3)
# [0.8, 1.0, 0.4]
random.sample(list(mit.numeric_range(10.0, 20.0, 0.25)), 10)
# [17.25, 12.0, 19.75, 14.25, 15.25, 12.75, 14.5, 15.75, 13.5, 18.25]
random.uniform generate float values
import random
def get_random(low,high,length):
lst = []
while len(lst) < length:
lst.append(random.uniform(low,high))
lst = list(set(lst))
return lst
我知道有一些简单的方法可以生成唯一随机整数列表(例如 random.sample(range(1, 100), 10)
)。
我想知道是否有更好的方法来生成唯一随机浮点数列表,除了编写一个函数,它的作用类似于一个范围,但接受这样的浮点数:
import random
def float_range(start, stop, step):
vals = []
i = 0
current_val = start
while current_val < stop:
vals.append(current_val)
i += 1
current_val = start + i * step
return vals
unique_floats = random.sample(float_range(0, 2, 0.2), 3)
有更好的方法吗?
回答
一个简单的方法是保留一组到目前为止看到的所有随机值,如果有重复则重新选择:
import random
def sample_floats(low, high, k=1):
""" Return a k-length list of unique random floats
in the range of low <= x <= high
"""
result = []
seen = set()
for i in range(k):
x = random.uniform(low, high)
while x in seen:
x = random.uniform(low, high)
seen.add(x)
result.append(x)
return result
备注
这项技术是 Python 自己的 random.sample() 的实现方式。
该函数使用 set 来跟踪之前的选择,因为搜索集合的复杂度为 O(1),而搜索列表的复杂度为 O(n)。
计算重复选择的概率等同于著名的Birthday Problem。
给定来自 random() 的 2**53 个不同的可能值,重复项很少见。 平均而言,重复浮点数约为 120,000,000 个样本。
变体:有限的浮动范围
如果人口仅限于一系列均匀分布的浮点数,则可以使用 random.sample() directly. The only requirement is that the population be a Sequence:
from __future__ import division
from collections import Sequence
class FRange(Sequence):
""" Lazily evaluated floating point range of evenly spaced floats
(inclusive at both ends)
>>> list(FRange(low=10, high=20, num_points=5))
[10.0, 12.5, 15.0, 17.5, 20.0]
"""
def __init__(self, low, high, num_points):
self.low = low
self.high = high
self.num_points = num_points
def __len__(self):
return self.num_points
def __getitem__(self, index):
if index < 0:
index += len(self)
if index < 0 or index >= len(self):
raise IndexError('Out of range')
p = index / (self.num_points - 1)
return self.low * (1.0 - p) + self.high * p
这里是从 10.0 到 20.0 的 41 个均匀间隔的浮点数范围内选择十个随机样本而不放回的示例。
>>> import random
>>> random.sample(FRange(low=10.0, high=20.0, num_points=41), k=10)
[13.25, 12.0, 15.25, 18.5, 19.75, 12.25, 15.75, 18.75, 13.0, 17.75]
您可以轻松地使用整数列表生成浮点数:
int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
查看this Stack Overflow question关于生成随机浮点数的信息。
如果您希望它与 python2 一起使用,请添加此导入:
from __future__ import division
您可以直接使用 random.uniform(start, stop)
。使用双精度浮点数,如果您的集合很小,您可以相对确定它们是唯一的。如果您想生成大量随机浮点数并且需要避免您有一个数字两次,请在将它们添加到列表之前进行检查。
但是,如果您正在寻找特定数字的选择,这不是解决方案。
min_val=-5
max_val=15
numpy.random.random_sample(15)*(max_val-min_val) + min_val
或使用制服
numpy.random.uniform(min_val,max_val,size=15)
如文档中所述 Python 具有 random.random() 函数:
import random
random.random()
然后你会得到一个浮点值:0.672807098390448
所以你需要做的就是做一个for
循环并打印出random.random():
>>> for i in range(10):
print(random.random())
如果需要保证唯一性,可能效率更高
- 尝试立即在
[lo, hi]
中生成n
个随机浮点数。 - 如果唯一浮点数的长度不是
n
,请尝试生成,但是仍然需要很多浮点数
并相应地继续直到你有足够的,而不是在 Python 级别循环检查集合中逐一生成它们。
如果您负担得起 NumPy,那么使用 np.random.uniform
可以大大加快速度。
import numpy as np
def gen_uniq_floats(lo, hi, n):
out = np.empty(n)
needed = n
while needed != 0:
arr = np.random.uniform(lo, hi, needed)
uniqs = np.setdiff1d(np.unique(arr), out[:n-needed])
out[n-needed: n-needed+uniqs.size] = uniqs
needed -= uniqs.size
np.random.shuffle(out)
return out.tolist()
如果您不能使用 NumPy,它仍然可能更有效,具体取决于您的数据需要应用相同的概念,即事后检查重复项,维护一个集合。
def no_depend_gen_uniq_floats(lo, hi, n):
seen = set()
needed = n
while needed != 0:
uniqs = {random.uniform(lo, hi) for _ in range(needed)}
seen.update(uniqs)
needed -= len(uniqs)
return list(seen)
粗略基准
极端退化案例
# Mitch's NumPy solution
%timeit gen_uniq_floats(0, 2**-50, 1000)
<b>153 µs ± 3.71 µs per loop</b> (mean ± std. dev. of 7 runs, 10000 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 2**-50, 1000)
<b>495 µs ± 43.9 µs per loop</b> (mean ± std. dev. of 7 runs, 1000 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 2**-50, 1000)
<b>618 µs ± 13 µs per loop</b> (mean ± std. dev. of 7 runs, 1000 loops each)
更多"normal"案例(样本较大)
# Mitch's NumPy solution
%timeit gen_uniq_floats(0, 1, 10**5)
<b>15.6 ms ± 1.12 ms per loop</b> (mean ± std. dev. of 7 runs, 100 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 1, 10**5)
<b>65.7 ms ± 2.31 ms per loop</b> (mean ± std. dev. of 7 runs, 10 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 1, 10**5)
<b>78.8 ms ± 4.22 ms per loop</b> (mean ± std. dev. of 7 runs, 10 loops each)
more_itertools
has a generic numeric_range
处理整数和浮点数。
import random
import more_itertools as mit
random.sample(list(mit.numeric_range(0, 2, 0.2)), 3)
# [0.8, 1.0, 0.4]
random.sample(list(mit.numeric_range(10.0, 20.0, 0.25)), 10)
# [17.25, 12.0, 19.75, 14.25, 15.25, 12.75, 14.5, 15.75, 13.5, 18.25]
random.uniform generate float values
import random
def get_random(low,high,length):
lst = []
while len(lst) < length:
lst.append(random.uniform(low,high))
lst = list(set(lst))
return lst