web-dev-qa-db-ja.com

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)

これを行うより良い方法はありますか?

14
Simon

回答

簡単な方法の1つは、これまでに見たすべてのランダムな値のセットを保持し、繰り返しがある場合は再選択することです。

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()の実装方法です。

  • リストの検索がO(n)であるときにセットの検索はO(1))であるため、関数は set を使用して以前の選択を追跡します。

  • 重複する選択の確率を計算することは、有名な Birthday Problem と同等です。

  • random()から2 ** 53の異なる可能な値が与えられた場合、重複はまれです。平均すると、約120,000,000のサンプルでフロートが重複することが予想されます。

バリエーション:制限された浮動小数点範囲

人口が等間隔のフロートの範囲のみに制限されている場合は、 random.sample() を使用できます。直接。唯一の要件は、母集団が 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の等間隔のフロートの範囲から、置換せずに10個のランダムサンプルを選択する例です。

>>> 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]
17

整数のリストを使用して、浮動小数点数を簡単に生成できます。

int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]

ランダムな浮動小数点数の生成について このスタックオーバーフローの質問 を確認してください。

Python2で動作させる場合は、次のインポートを追加します。

from __future__ import division
5
Or Duan

一意性を保証する必要がある場合は、

  1. nランダムフロートを[lo, hi]で一度に生成してみてください。
  2. 一意のフロートの長さがnではない場合、試行して生成しますが、それでも多くのフロートが必要です。

Pythonセットに対するレベルループチェックでそれらを1行1列生成するのではなく、十分になるまで続けます。

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)
5
miradulo

random.uniform(start, stop)を使用できます。倍精度のフロートを使用すると、セットが小さい場合は、それらが一意であることを比較的確信できます。多数のランダムなフロートを生成する必要があり、数値が2度あることを回避する必要がある場合は、リストに追加する前に確認してください。

ただし、特定の番号の選択を探している場合、これは解決策ではありません。

4
C. Nitschke
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)
1
Joran Beasley

ドキュメントに記載されているようにPythonにはrandom.random()関数があります:

import random
random.random()

次に、次のようにfloat値を取得します:0.672807098390448

したがって、forループを作成して、random.random()を出力するだけです。

>>> for i in range(10):
print(random.random())
1
aviad

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]
0
pylang