Как создать список уникальных случайных поплавков в 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)
есть ли лучший способ сделать это?
7 ответов
ответ
один простой способ-сохранить набор всех случайных значений, видимых до сих пор, и повторно выбрать, если есть повторение:
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 случайные.sample () есть.
функция использует set чтобы отслеживать предыдущие выборы, потому что поиск набора-O(1), а поиск списка-O (n).
вычислительной вероятность повторного выбора эквивалентна известной Проблема Рождения.
учитывая 2 * * 53 различных возможных значений из random () повторы редко. В среднем вы можете ожидать дубликат float примерно на 120,000,000 образцов.
вариант: ограниченный диапазон поплавкового
если население ограничено только диапазоном равномерно расположенных поплавков, то можно использовать случайные.sample () напрямую. Единственное требование, чтобы население было последовательность:
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
вот пример выбора десяти случайных выборок без замены из диапазона 41 равномерно расположенных поплавков от 10.0 до 20.0.
>>> 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]
Если вам нужно, чтобы гарантировать уникальность, это может быть более эффективно
- попробуйте создать
n
случайные плавает в[lo, hi]
сразу. - если длина уникальных поплавков не
n
, попробуйте создать, сколько поплавков все еще необходимо
и продолжайте соответственно, пока у вас не будет достаточно, в отличие от генерации их 1 на 1 в цикле уровня 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)
153 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 2**-50, 1000)
495 µs ± 43.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 2**-50, 1000)
618 µs ± 13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
более "нормальный" случай (С более большим образцом)
# Mitch's NumPy solution
%timeit gen_uniq_floats(0, 1, 10**5)
15.6 ms ± 1.12 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
# Mitch's Python-only solution
%timeit no_depend_gen_uniq_floats(0, 1, 10**5)
65.7 ms ± 2.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Raymond Hettinger's solution (single number generation)
%timeit sample_floats(0, 1, 10**5)
78.8 ms ± 4.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Вы можете легко использовать ваш список целых чисел для создания поплавков:
int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
проверить этот вопрос переполнения стека о генерации случайных поплавки.
Если вы хотите, чтобы он работал с 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 ():
import random
random.random()
тогда вы получите float val как: 0.672807098390448
поэтому все, что вам нужно сделать, это сделать for
loop и распечатать случайным образом.random():
>>> for i in range(10):
print(random.random())
more_itertools
есть универсальный 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]