Ich weiß, dass es einfache Möglichkeiten gibt, Listen von zufälligen Ganzzahlen (z. B. random.sample(range(1, 100), 10)
) zu generieren.
Ich frage mich, ob es eine bessere Möglichkeit gibt, eine Liste eindeutiger zufälliger Floats zu erstellen, abgesehen davon, dass Sie eine Funktion schreiben, die sich wie ein Bereich verhält, aber Floats wie folgt akzeptiert:
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)
Gibt es einen besseren Weg, dies zu tun?
Eine einfache Möglichkeit besteht darin, eine Reihe aller bisher gesehenen Zufallswerte beizubehalten und erneut zu wählen, ob es eine Wiederholung gibt:
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
Mit dieser Technik wird Pythons eigenes random.sample () implementiert.
Die Funktion verwendet ein set , um die vorherige Auswahl zu verfolgen, da die Suche nach einem Satz O(1) und das Durchsuchen einer Liste O (n) ist.
Das Berechnen der Wahrscheinlichkeit einer doppelten Auswahl entspricht dem bekannten Geburtstagsproblem .
Bei 2 ** 53 verschiedenen möglichen Werten von random () sind Duplikate selten. Im Durchschnitt können Sie mit einem Duplikat von etwa 120.000.000 Proben rechnen.
Wenn die Grundgesamtheit auf einen Bereich von gleichmäßig verteilten Floats beschränkt ist, können Sie random.sample () direkt verwenden. Die einzige Voraussetzung ist, dass die Population eine Sequenz ist:
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
Hier ein Beispiel für die Auswahl von zehn Stichproben ohne Ersatz aus einem Bereich von 41 gleichmäßig verteilten Floats von 10,0 bis 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]
Sie können Ihre Liste ganzer Zahlen ganz einfach verwenden, um Floats zu generieren:
int_list = random.sample(range(1, 100), 10)
float_list = [x/10 for x in int_list]
Check out this Stack Overflow frage über das Erzeugen von Zufallsbewegungen.
Wenn Sie möchten, dass es mit python2 arbeitet, fügen Sie diesen Import hinzu:
from __future__ import division
Wenn Sie die Eindeutigkeit garantieren müssen, kann dies effizienter sein
n
zufällige Floats gleichzeitig in [lo, hi]
zu generieren.n
ist, versuchen und generieren Sie, obwohl noch viele Floats erforderlich sind und fahren Sie entsprechend fort, bis Sie genug haben, anstatt sie 1-mal-1 in einer Python-Level-Schleife zu erzeugen, die eine Menge überprüft.
Wenn Sie sich NumPy leisten können, kann dies mit np.random.uniform
eine enorme Beschleunigung sein.
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()
Wenn Sie NumPy nicht verwenden können, kann es dennoch effizienter sein, abhängig von Ihren Datenanforderungen, dass Sie das gleiche Konzept anwenden, um nach Dupes zu suchen.
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)
Extrem entarteter Fall
# 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)
Mehr "normaler" Fall (mit größerem Muster)
# 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)
Sie können einfach random.uniform(start, stop)
verwenden. Mit doppelt präzisen Schwimmern können Sie relativ sicher sein, dass sie einzigartig sind, wenn Ihr Set klein ist. Wenn Sie eine große Anzahl zufälliger Floats generieren möchten und vermeiden möchten, dass Sie eine Zahl zweimal haben, überprüfen Sie diese, bevor Sie sie der Liste hinzufügen.
Wenn Sie jedoch nach einer Auswahl bestimmter Zahlen suchen, ist dies nicht die Lösung.
min_val=-5
max_val=15
numpy.random.random_sample(15)*(max_val-min_val) + min_val
oder einheitliche verwenden
numpy.random.uniform(min_val,max_val,size=15)
Wie in der Dokumentation angegeben, hat Python die Funktion random.random ():
import random
random.random()
Dann erhalten Sie einen Float-Wert als: 0,672807098390448
Sie müssen also nur eine for
-Schleife erstellen und random.random () ausdrucken:
>>> for i in range(10):
print(random.random())
more_itertools
hat ein generisches numeric_range
, das sowohl Ganzzahlen als auch Floats verarbeitet.
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]