一意のランダムな整数のリストを生成する簡単な方法があることを知っています(例: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)
これを行うより良い方法はありますか?
簡単な方法の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]
整数のリストを使用して、浮動小数点数を簡単に生成できます。
int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
ランダムな浮動小数点数の生成について このスタックオーバーフローの質問 を確認してください。
Python2で動作させる場合は、次のインポートを追加します。
from __future__ import division
一意性を保証する必要がある場合は、
n
ランダムフロートを[lo, hi]
で一度に生成してみてください。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)
random.uniform(start, stop)
を使用できます。倍精度のフロートを使用すると、セットが小さい場合は、それらが一意であることを比較的確信できます。多数のランダムなフロートを生成する必要があり、数値が2度あることを回避する必要がある場合は、リストに追加する前に確認してください。
ただし、特定の番号の選択を探している場合、これは解決策ではありません。
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()
次に、次のようにfloat値を取得します:0.672807098390448
したがって、for
ループを作成して、random.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]