如何在Python中生成唯一随机浮点数列表

Sim*_*ach 13 python random distinct-values

我知道有很简单的方法可以生成唯一随机整数列表(例如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)
Run Code Online (Sandbox Code Playgroud)

有一个更好的方法吗?

Ray*_*ger 17

回答

一种简单的方法是保留一组到目前为止看到的所有随机值,并重新选择是否有重复:

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
Run Code Online (Sandbox Code Playgroud)

笔记

  • 这种技术是Python自己的random.sample()的实现方式.

  • 该函数使用一个来跟踪先前的选择,因为搜索一个集合是O(1),而搜索列表是O(n).

  • 计算重复选择的概率等同于着名的生日问题.

  • 给定来自random()的 2**53个不同的可能值,重复是不常见的.平均而言,您可以预期大约120,000,000个样本的重复浮动.

变型:有限浮动范围

如果总体仅限于一系列均匀间隔的浮点数,则可以直接使用random.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
Run Code Online (Sandbox Code Playgroud)

下面是一个选择十个随机样本而无需从41到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]
Run Code Online (Sandbox Code Playgroud)

  • 这就是我所建议的...设置查找是O(1)...并且随机浮动很容易 (3认同)
  • @RaymondHettinger同意了!我不知道,我只是得到的印象99%的人认为他们需要"独特的随机花车"并不真正需要"独特性",所以我希望他们从这个问题/答案中得不到错误的想法.没有从连续分布中取代的取样不是我曾经遇到的事情 - 如果你有一个病理上很小的间隔,那么由于某种原因可能是必要的. (2认同)
  • @StefanPochmann要超过2 ^ 53,你需要使用指数中的位.这将导致随机浮点数不再被分配. (2认同)
  • 我是第二个Mitch说的:我不认为从连续分布中采样唯一浮点数的代码是有用的.如果你可以随意关闭浮动,但它们不能相同,你可能会做出一些错误的假设. (2认同)

Or *_*uan 5

您可以轻松使用整数列表生成浮点数:

int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
Run Code Online (Sandbox Code Playgroud)

查看有关生成随机浮点数的Stack Overflow问题.

如果您希望它与python2一起使用,请添加以下导入:

from __future__ import division
Run Code Online (Sandbox Code Playgroud)


mir*_*ulo 5

如果您需要保证唯一性,可能会更有效率

  1. 尝试立即生成n随机浮点数[lo, hi].
  2. 如果没有独特浮子的长度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()
Run Code Online (Sandbox Code Playgroud)

如果您不能使用NumPy,它仍然可能更高效,具体取决于您的数据需要应用相同的检查dupes的概念,维护一组.

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)
Run Code Online (Sandbox Code Playgroud)

粗略的基准

极端堕落的情况

# 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)
Run Code Online (Sandbox Code Playgroud)

更"正常"的情况(样本量更大)

# 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)
Run Code Online (Sandbox Code Playgroud)