C'est en fait un problème plus difficile que cela ne semble, principalement parce que de nombreuses solutions mathématiquement correctes échoueront à vous permettre d'atteindre toutes les possibilités (plus d'informations ci-dessous).
Tout d'abord, voici quelques solutions faciles à implémenter, correctes si vous disposez d'un générateur de nombres vraiment aléatoire :
(0) La réponse de Kyle, qui est O(n).
(1) Générez une liste de n paires [(0, rand), (1, rand), (2, rand), ...], triez-les selon la deuxième coordonnée, et utilisez les k premiers (pour vous, k=5) indices pour obtenir votre sous-ensemble aléatoire. Je pense que c'est facile à implémenter, bien que cela prenne du temps en O(n log n).
(2) Initialisez une liste vide s = [] qui contiendra les indices de k éléments aléatoires. Choisissez un nombre r dans {0, 1, 2, ..., n-1} de manière aléatoire, r = rand % n, et ajoutez-le à s. Ensuite, prenez r = rand % (n-1) et insérez-le dans s ; ajoutez à r le nombre d'éléments inférieurs à lui dans s pour éviter les collisions. Puis prenez r = rand % (n-2), et faites la même chose, etc. jusqu'à obtenir k éléments distincts dans s. Cela a un temps d'exécution dans le pire des cas de O(k^2). Donc pour k << n, cela peut être plus rapide. Si vous gardez s trié et suivez les intervalles contigus qu'il contient, vous pouvez l'implémenter en O(k log k), mais cela demande plus de travail.
@Kyle - tu as raison, poussé par l'erreur, je suis d'accord avec ta réponse. Je l'ai rapidement lue au début, et j'ai pensé à tort que tu indiquais de choisir séquentiellement chaque élément avec une probabilité fixe k/n, ce qui aurait été faux - mais ton approche adaptative me semble correcte. Désolé pour ça.
D'accord, et maintenant, pour la surprise : asymptotiquement (pour k fixé, n croissant), il y a n^k/k! choix de sous-ensemble de k éléments parmi n éléments [c'est une approximation de (n choose k)]. Si n est grand et que k n'est pas très petit, alors ces nombres sont énormes. La meilleure longueur de cycle que vous pouvez espérer dans n'importe quel générateur de nombres aléatoires standard sur 32 bits est 2^32 = 256^4. Donc si nous avons une liste de 1000 éléments et que nous voulons en choisir 5 au hasard, il n'y a aucun moyen pour un générateur de nombres aléatoires standard d'atteindre toutes les possibilités. Cependant, tant que vous acceptez un choix qui fonctionne bien pour des ensembles plus petits, et qui semble toujours aléatoire, alors ces algorithmes devraient être bons.
Addendum : Après avoir écrit cela, j'ai réalisé qu'il est difficile de mettre en œuvre l'idée (2) correctement, donc je voulais clarifier cette réponse. Pour obtenir un temps en O(k log k), vous avez besoin d'une structure de type tableau qui prend en charge des recherches et des insertions en O(log m) - un arbre binaire équilibré peut le faire. En utilisant une telle structure pour construire un tableau appelé s, voici un pseudocode en python :
# Retourne un conteneur s avec k nombres aléatoires distincts de {0, 1, ..., n-1}
def ChooseRandomSubset(n, k):
for i in range(k):
r = UniformRandom(0, n-i) # Peut être 0, doit être < n-i
q = s.FirstIndexSuchThat( s[q] - q > r ) # C'est la recherche.
s.InsertInOrder(q ? r + q : r + len(s)) # Insère juste avant q.
return s
Je suggère de parcourir quelques cas d'exemple pour voir comment cela implémente efficacement l'explication ci-dessus en anglais.
15 votes
Par « aléatoire », entendez-vous inclusif ou exclusif? En d'autres termes, un même élément peut-il être choisi plus d'une fois? (vraiment aléatoire) Ou une fois qu'un élément est choisi, doit-il être retiré du pool disponible et ne plus être choisi?
0 votes
Très similaire : Choisir N éléments au hasard dans une séquence de longueur inconnue, Algorithme pour sélectionner une seule combinaison aléatoire de valeurs?
0 votes
??? vous venez de mélanger et de prendre les premiers N .. pourquoi y a-t-il tant de discussion ici?
0 votes
@Fattie Ceci est valable dans les cas où le mélange est extrêmement inefficace (par exemple, si la liste est énorme) ou si vous n'êtes pas autorisé à modifier l'ordre de la liste originale.
0 votes
@uckelman la question ne dit absolument rien à ce sujet. En ce qui concerne la solution la plus efficace à ce problème pour des ensembles extrêmement importants (et notez qu'il est tout à fait inconcevable d'utiliser quelque chose comme "List" dans de tels cas), cela dépend du domaine de taille. Notez que la réponse acceptée est désespérément fausse.
0 votes
La réponse acceptée n'est pas désespérément fausse. Elle n'est même pas fausse. Voir ici: stackoverflow.com/questions/35065764/… Les considérations du cas d'utilisation ne sont pas sans importance simplement parce qu'elles ne sont pas mentionnées.
0 votes
@Fattie Peut-être donner un argument que la réponse acceptée est incorrecte, plutôt que de le prétendre sans en donner un ?
0 votes
La meilleure méthode est probablement l'échantillonnage de réservoir, au fait : fr.wikipedia.org/wiki/Échantillonnage_de_réservoir
0 votes
Salut @uckelman, merci, il y a déjà de vastes discussions soulignant les problèmes évidents; l'échantillonnage du réservoir n'est utile que dans (comme je l'ai dit) certains domaines (en fait, entièrement détaillé dans la 2ème phrase actuelle de l'article wiki). La question posée concerne spécifiquement une
List
spécifiquement enC#
et l'utilisateur veut spécifiquement une solution rapide et simple. (évidemment, la réponse est de trier et de prendre cinq. ce serait de l'ingénierie extrêmement mauvaise si vous faisiez autre chose que cela dans des domaines allant jusqu'à disons, oh, 10 000 éléments. notez que bien sûr vous pouvez inventer ...0 votes
... situations incroyablement obscures où vous ne le feriez pas et c'est bien ainsi. cela serait et est le sujet de nombreuses questions algorithmiques par exemple sur le génie logiciel. lorsque quelqu'un fournit la bonne réponse ici (les deux mots de la bonne réponse), bien sûr, vous pouvez mentionner dans une note que dans des situations incroyablement obscures vous ne le feriez pas. {évidemment, tout programmeur travaillant saurait que si la liste est relativement énorme, vous utiliseriez simplement l'algorithme de choix indéterminé, et vous pourriez donner deux lignes de code pour expliquer cela, mais encore une fois, bien sûr vous pouvez ENSUITE construire des situations où vous
0 votes
... utilisent hadoop et des GPUs ou quelque chose et ensuite dans cet domaine, vous devriez analyser quelle, comme vous dites, approche d'échantillonnage de réservoir (parmi les nombreuses et la recherche continue dans ce domaine) est la meilleure.)) Pour rendre la situation plus franche, en regardant la "réponse" cochée. Disons que c'était un projet réel, comme une équipe travaillant sur un jeu chez Nintendo ou quelque chose du genre. Il y a "40" comme dans la réponse (rofl) chars sur le champ de bataille et 5 doivent être choisis au hasard. Un des programmeurs commence à écrire cette solution - il serait immédiatement renvoyé! Gosh. L'ingénierie inappropriée est incroyablement mauvaise ingénierie.
0 votes
@Fattie La vaste discussion soulignant les problèmes "évidents" est en fait le problème.
0 votes
@Fattie De plus, si vous pensez que l'échantillonnage de réservoir n'est "utile que dans certains domaines", je suggère de lire au-delà de la deuxième phrase de l'article Wikipedia. L'algorithme donné sous le titre "Un algorithme optimal" est court, simple et généralement applicable.
0 votes
("domains" ici est une façon élégante de dire "combien d'éléments". l'approche mentionnée est totalement hors de propos sur moins de, disons, quelques centaines d'articles. si vous n'êtes pas familier avec l'échantillonnage de réservoir et ne l'avez pas utilisé auparavant, la première phrase de l'article décrit clairement ce à quoi il se rapporte: "une population de taille inconnue n en un seul passage sur les éléments. La taille de la population n n'est pas connue de l'algorithme et est généralement [plus grande que les tailles de RAM]" cela n'a littéralement aucun lien avec ce qui est discuté ici.)