Simple à code O(N + K*log(K)) de manière à
Prendre un échantillon aléatoire sans remplacement de l'indice, trier les indices, et de les prendre à partir de l'original.
indices = random.sample(range(len(myList)), K)
[myList[i] for i in sorted(indices)]
Ou de façon plus concise:
[x[1] for x in sorted(random.sample(enumerate(myList),K))]
Optimisé O(N) en temps O(1)-auxiliaire-de l'espace de façon
Alternativement, vous pouvez utiliser un math astuce et de manière itérative passer par myList
de gauche à droite, choisir les numéros avec les dynamiques de probabilité (N-numbersPicked)/(total-numbersVisited)
. L'avantage de cette approche est qu'il est un O(N)
de l'algorithme, car il n'implique pas de du tri!
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
La preuve de concept et de tester que les probabilités sont corrects:
Simulé avec 1 billion de pseudo-aléatoires d'échantillons au cours de 5 heures:
>>> 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
})
Les probabilités de s'écarter de la vraie probabilités au moins un facteur de 1.0001. L'exécution de ce test de nouveau entraîné dans un ordre différent du sens qu'il n'est pas biaisé en faveur d'une commande. Exécution du test avec moins d'échantillons pour l' [0,1,2,3,4], k=3
et [0,1,2,3,4,5], k=4
avaient des résultats similaires.
edit: je ne sais Pas pourquoi les gens sont à droit de vote jusqu'mauvais commentaires ou peur de upvote... NON, il n'y a rien de mal avec cette méthode. =)