Einfach zu codierender O (N + K * log (K)) Weg
Nehmen Sie eine Zufallsstichprobe ohne Ersatz der Indizes, sortieren Sie die Indizes und entnehmen Sie sie dem Original.
indices = random.sample(range(len(myList)), K)
[myList[i] for i in sorted(indices)]
Oder genauer:
[x[1] for x in sorted(random.sample(enumerate(myList),K))]
Optimierte O (N) -Zeit, O (1) -Intiliilraum-Weg
Sie können alternativ einen mathematischen Trick und iterativ durchläuft myList
von links nach rechts, Kommissionierung Zahlen mit dynamisch wechselnden Wahrscheinlichkeit (N-numbersPicked)/(total-numbersVisited)
. Der Vorteil dieses Ansatzes ist, dass es sich um einen O(N)
Algorithmus handelt, da keine Sortierung erforderlich ist!
from __future__ import division
def orderedSampleWithoutReplacement(seq, k):
if not 0<=k<=len(seq):
raise ValueError('Required that 0 <= sample_size <= population_size')
numbersPicked = 0
for i,number in enumerate(seq):
prob = (k-numbersPicked)/(len(seq)-i)
if random.random() < prob:
yield number
numbersPicked += 1
Proof of Concept und Test der Richtigkeit der Wahrscheinlichkeiten :
Simuliert mit 1 Billion Pseudozufallsstichproben über einen Zeitraum von 5 Stunden:
>>> Counter(
tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
for _ in range(10**9)
)
Counter({
(0, 3): 166680161,
(1, 2): 166672608,
(0, 2): 166669915,
(2, 3): 166667390,
(1, 3): 166660630,
(0, 1): 166649296
})
Die Wahrscheinlichkeiten weichen um einen Faktor von 1.0001 von den tatsächlichen Wahrscheinlichkeiten ab. Das erneute Ausführen dieses Tests führte zu einer anderen Reihenfolge, was bedeutet, dass er nicht auf eine Bestellung ausgerichtet ist. Durchführen des Tests mit weniger Proben für [0,1,2,3,4], k=3
und [0,1,2,3,4,5], k=4
ähnliche Ergebnisse.
edit: Ich bin mir nicht sicher, warum Leute falsche Kommentare abgeben oder Angst haben, zu stimmen ... NEIN, an dieser Methode ist nichts auszusetzen. =)
(Auch ein nützlicher Hinweis von Benutzer tegan in den Kommentaren: Wenn dies python2 ist, sollten Sie xrange wie gewohnt verwenden, wenn Sie sich wirklich für zusätzlichen Speicherplatz interessieren.)
edit : Beweis: In Anbetracht der gleichmäßigen Verteilung (ohne Ersatz) der Auswahl einer Teilmenge k
aus einer Population seq
von Größe len(seq)
können wir eine Partition an einem beliebigen Punkt i
in 'links' (0,1, ..., i-1) betrachten. und 'richtig' (i, i + 1, ..., len (seq)). Da wir numbersPicked
aus der linken bekannten Teilmenge ausgewählt haben, müssen die verbleibenden aus derselben gleichmäßigen Verteilung in der rechten unbekannten Teilmenge stammen, obwohl die Parameter jetzt unterschiedlich sind. Insbesondere ist die Wahrscheinlichkeit, seq[i]
die ein ausgewähltes Element enthält,#remainingToChoose/#remainingToChooseFrom
, oder(k-numbersPicked)/(len(seq)-i)
Also simulieren wir das und greifen auf das Ergebnis zurück. (Dies muss beendet werden, da bei #remainingToChoose == #remainingToChooseFrom alle verbleibenden Wahrscheinlichkeiten 1 sind.) Dies ähnelt einem Wahrscheinlichkeitsbaum, der zufällig dynamisch generiert wird. Grundsätzlich können Sie eine gleichmäßige Wahrscheinlichkeitsverteilung simulieren, indem Sie auf vorherige Entscheidungen konditionieren (wenn Sie den Wahrscheinlichkeitsbaum vergrößern, wählen Sie die Wahrscheinlichkeit des aktuellen Zweigs so aus, dass sie aposteriori mit früheren Blättern identisch ist, dh von vorherigen Entscheidungen abhängig ist; dies funktioniert, weil diese Wahrscheinlichkeit ist einheitlich genau N / k).
edit : Timothy Shields erwähnt Reservoir Sampling , die Verallgemeinerung dieser Methode, wenn sie len(seq)
unbekannt ist (z. B. mit einem Generatorausdruck). Insbesondere ist der als "Algorithmus R" bezeichnete O (N) - und O (1) -Raum, wenn er an Ort und Stelle durchgeführt wird; Dabei wird das erste N-Element genommen und langsam ersetzt (ein Hinweis auf einen induktiven Beweis wird ebenfalls gegeben). Auf der Wikipedia-Seite finden Sie auch nützliche verteilte Varianten und verschiedene Varianten der Reservoir-Probenahme.
Bearbeiten : Hier ist eine andere Möglichkeit, es unten semantisch offensichtlicher zu codieren.
from __future__ import division
import random
def orderedSampleWithoutReplacement(seq, sampleSize):
totalElems = len(seq)
if not 0<=sampleSize<=totalElems:
raise ValueError('Required that 0 <= sample_size <= population_size')
picksRemaining = sampleSize
for elemsSeen,element in enumerate(seq):
elemsRemaining = totalElems - elemsSeen
prob = picksRemaining/elemsRemaining
if random.random() < prob:
yield element
picksRemaining -= 1
from collections import Counter
Counter(
tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
for _ in range(10**5)
)
random.sample
und dann sortieren?