Il y a une très belle et algorithme efficace pour cela à l'aide d'une méthode appelée réservoir d'échantillonnage.
Permettez-moi de commencer en vous donnant son histoire:
Knuth appelle cela un Algorithme de R sur p. 144 de son édition de 1997 de Seminumerical Algorithmes (volume 2 de The Art of Computer Programming), et fournit un code pour cela. Knuth attributs de l'algorithme d'Alan G. Waterman. Malgré une longue recherche, je n'ai pas été en mesure de trouver Waterman document d'origine, si elle existe, ce qui peut être la raison pour laquelle vous aurez le plus souvent voir Knuth cité comme la source de cet algorithme.
McLeod et Bellhouse, 1983 (1) fournir une discussion plus approfondie de Knuth ainsi que le premier publié la preuve (que je connais) que l'algorithme fonctionne.
Vitter 1985 (2) examens de l'Algorithme de R et présente ensuite les trois autres algorithmes qui fournissent le même résultat, mais avec une torsion. Plutôt que de faire un choix d'inclure ou ignorer chaque élément entrant, son algorithme détermine le nombre de nouveaux éléments pour être ignorée. Dans ses essais (ce qui, certes, ne sont pas à jour maintenant) cette diminution du temps d'exécution de façon spectaculaire en évitant la génération de nombres aléatoires et des comparaisons sur chaque de numéro.
Dans le pseudo-code de l'algorithme est:
Let R by the result array of size s
Let I be an input queue
> Fill the reservoir array
for j in the range [1,s]:
R[j]=I.pop()
elements_seen=s
while I is not empty:
elements_seen+=1
j=random(1,elements_seen) > This is inclusive
if j<=s:
R[j]=I.pop()
else:
I.pop()
Notez que j'ai précisément écrit le code pour éviter de spécifier la taille de l'entrée. C'est l'une des cool propriétés de cet algorithme: vous pouvez l'exécuter sans avoir besoin de connaître la taille de l'entrée à l'avance et encore vous assure que chaque élément que vous rencontrez a une probabilité égale de se retrouver en R
(qui est, il n'y a pas de parti pris). En outre, R
contient une juste et de l'échantillon représentatif de l'ensemble des éléments de l'algorithme a examiné en tout temps. Cela signifie que vous pouvez l'utiliser comme un algorithme en ligne.
Pourquoi ce travail?
McLeod et Bellhouse (1983) de fournir une preuve en utilisant les mathématiques de combinaisons. C'est joli, mais il pourrait être un peu difficile de reconstruire ici. Donc, j'ai généré une preuve alternative qui est plus facile à expliquer.
Nous procédons par une preuve par induction.
Disons que nous voulons générer un ensemble de s
- éléments et que nous avons déjà vu, n>s
- éléments.
Supposons que notre actuel s
des éléments ont déjà été choisis avec le plus de probabilité s/n
.
Par la définition de l'algorithme, nous avons choisi l'élément n+1
avec une probabilité s/(n+1)
.
Chaque élément déjà partie de notre jeu de résultats a une probabilité 1/s
d'être remplacé.
La probabilité qu'un élément de l' n
-vu ensemble de résultats est remplacé dans l' n+1
-vu de résultat est, par conséquent, (1/s)*s/(n+1)=1/(n+1)
. À l'inverse, la probabilité qu'un élément n'est pas remplacé est - 1-1/(n+1)=n/(n+1)
.
Ainsi, l' n+1
-vu ensemble de résultats contient un élément que ce soit s'il faisait partie de l' n
-vu ensemble de résultats et n'a pas été remplacé---cette probabilité est - (s/n)*n/(n+1)=s/(n+1)
---ou si l'élément a été choisi---avec une probabilité s/(n+1)
.
La définition de l'algorithme nous dit que la première s
des éléments sont automatiquement inclus dès la première n=s
des membres de l'ensemble de résultats. Par conséquent, l' n-seen
résultat inclut chaque élément avec s/n
(=1) probabilité de nous donner le nécessaire de base pour l'induction.
Références
McLeod, A. Ian, et David R. Bellhouse. "Une pratique de l'algorithme de dessin d'un échantillon aléatoire simple." Journal of the Royal Statistical Society. Série C (Statistiques Appliquées) 32.2 (1983): 182-184. (Lien)
Vitter, Jeffrey S. "échantillonnage Aléatoire avec un réservoir". ACM Transactions sur les Logiciels de Mathématiques (TOMS) 11.1 (1985): 37-57. (Lien)