web-dev-qa-db-de.com

So erstellen Sie eine Liste eindeutiger zufälliger Floats in Python

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?

12

Antworten

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

Anmerkungen

  • 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.

Variante: Eingeschränkter Float-Bereich

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

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
5
Or Duan

Wenn Sie die Eindeutigkeit garantieren müssen, kann dies effizienter sein

  1. Versuchen Sie, n zufällige Floats gleichzeitig in [lo, hi] zu generieren.
  2. Wenn die Länge der eindeutigen Floats nicht 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)

Grobe Benchmark

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

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.

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

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())
1
aviad

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