J'ai une table qui ressemble à celle-là: Ce que je veux faire est de sélectionner aléatoirement une ligne de cette table, mais en tenant compte du poids. Par exemple, si j'ai 3 lignes: p> La première ligne a 30% de chances d'être sélectionnée, la deuxième ligne a 20% de chances d'être sélectionné et la troisième rangée A 50% de chances d'être sélectionnée. P> Y a-t-il un moyen de faire cela? Si je dois exécuter 2 ou 3 requêtes, ce n'est pas un problème. P> p>
7 Réponses :
Je ne me souviens pas de la manière de RND () dans MySQL, mais ici Par exemple de travail pour MSSQL:
SELECT TOP(1) (weight +RAND ()) r, id, content, weight FROM Table ORDER BY 1 DESC
De cette façon, l'extérieur est aléatoire tout poids ;-)
UHM .. Maintenant, le hasard entre en jeu uniquement pour les lignes avec le poids le plus élevé.
;-) Maintenant, vous ajoutez 0 à 1 aléatoire au poids de 1000. Ce n'est pas vraiment utile. Le plus proche que vous puissiez obtenir à ce niveau de simplicité est si vous avez un poids multiple par numéro aléatoire. Mais si cela convient vraiment aux spécifications, il faut plus que je ne puisse pas le donner en ce moment être malade ;-)
D'accord, c'était du poids de 100, désolé, mais cela n'a pas vraiment d'importance ;-)
Sélectionnez *, Poids * Random () AS aléatoire () sous O de la commande de table par O de la limite de descente 1 code> est ce que je veux dire.
Sélectionnez * à partir de la commande de table en poids * Random () La limite de descente 1 code> est meilleure, plus courte et transfère moins de données ;-)
Cela ne fonctionne pas dans SQL Server, désolé. Dans un Select comme celui-ci, Rand () sera traité comme une expression constante et chaque ligne aura la même valeur. Vous pouvez le pirater en utilisant quelque chose comme la checksum (NewID ()), puis la normalisant à la plage que vous après. Genre de ecky mais devrait fonctionner.
Cowan, la question est étiquetée mysql code>, de toute façon ;-) Maintenant que mon commentaire a été évoqué, je me demande si je devrais poster mon commentaire comme une réponse sans que cela ne soit plus pensé ;-)
Hacker, oui, la question est MySQL, mais Dewfy a déclaré que c'était un «exemple de travail pour MSSQL», ce qui n'est pas. :)
Cowan, en effet, alors le bit de «travail» devrait être édité. "L'exemple non fonctionnel" semble mieux alors ;-)
Cela a le même problème que Nick F CODE> SHOWS ne fonctionne pas réellement et s'aggrave pour les plus grandes tables ..
Peut-être celui-ci: ou celui-ci: p>
Vous ignorez les poids, - les enregistrements avec un poids plus élevé suscient plus fréquemment dans le résultat.
Cela fonctionne dans MSSQL et je suis sûr qu'il devrait être possible de changer de couple de mots-clés pour le faire fonctionner dans MySQL (peut-être même plus agréable):
SELECT TOP 1 t.*
FROM @Table t
INNER JOIN (SELECT t.id, sum(tt.weight) AS cum_weight
FROM @Table t
INNER JOIN @Table tt ON tt.id <= t.id
GROUP BY t.id) tc
ON tc.id = t.id,
(SELECT SUM(weight) AS total_weight FROM @Table) tt,
(SELECT RAND() AS rnd) r
WHERE r.rnd * tt.total_weight <= tc.cum_weight
ORDER BY t.id ASC
Une approche simple (évitant les jointures ou les sous-requêtes) est de multiplier le poids par un nombre aléatoire compris entre 0 et 1 pour produire un poids temporaire à trier par: Mise à jour: cette méthode ne produit pas en fait les distributions correctes forte>, donc pour le moment rand () * 2x code> sera une valeur plus grande que rand () * x code> environ deux tiers du temps. Par conséquent, au fil du temps, chaque rangée doit être sélectionnée avec une fréquence proportionnelle à son poids relatif (par exemple, une ligne avec poids 100 sera sélectionnée environ 100 fois plus souvent qu'une rangée avec poids 1, etc.). Grève> p>
Cela fonctionne bien lorsque vous choisissez un faible nombre de lignes (best 2). J'ai besoin de choisir au hasard parmi 50 rangs. 1 Avoir un poids de 32, 1 un poids de 3 et 48 un poids de 1 pour une crise totale de 83. Donc, ma rangée de 32 devrait donc avoir une chance de 38,6% d'être choisie, mais avec cette méthode, il a 32 plus de chances Pour être choisi que tous ceux avec un poids de 1. Existe-t-il un moyen de prendre en compte le poids total? MERCI!!
Cela ne fonctionne-t-il pas dans votre cas? Dans votre cas, les risques de la rangée avec un poids de 32 étant choisi doivent être 32/83 (0,386 ou 38,6%). Les risques d'une rangée avec un poids de 1 étant choisi doivent être 1/83 (0,012 ou 1,2%). Mais depuis 32/83 = 32 * 1/83 b>, il est toujours le cas que la chose avec un poids de 32 doit être choisie 32 fois plus souvent qu'une chose avec un poids de 1!
J'ai peut-être commis une erreur dans mon script, mais j'avais plus de 30 fois la rangée avec un poids de 32 et des autres de temps en temps. Il a été choisi 32 fois plus souvent que tous les autres. J'ai terminé la création d'une table Temp avec le poids total, en l'utilisant pour avoir le poids en% (sélectionnez ID de près de50, total de l'ordre de poids par aléatoire () * (1 / (poids * 100 / total_weight.weight)).
Je suis désolé, je ne comprends pas le problème ici. Sûrement Devrait-il être choisi 32 fois plus souvent que les autres? C'est le comportement prévu de ma requête, mais cela convient également à ce que vous dites que vous attendez: car 38,6 = 32 * 1.2 b> C'est sûrement une autre façon de dire exactement la même chose I>! c'est à dire. Si vous vous attendez à ce que quelque chose arrive 38,6% du temps, alors par définition i> Vous devez vous attendre à ce qu'il arrive environ 32 fois plus souvent que quelque chose qui arrive 1,2% du temps. Je ne vois pas pourquoi votre table Temp est nécessaire. S'il vous plaît réfléchissez à soigneusement et assurez-vous qu'il y a vraiment un problème ici!
Je comprends ce que tu dis. Bien sûr, il devrait avoir 32 fois plus de chances d'être cueilli et tout autre avec un poids de 1. Ce que je dis, c'est que dans mon script, il a été choisi 32 fois plus souvent et tous les autres unis. Sur un test de 1000, j'avais quelque chose comme 960 fois celui avec le poids de 32 et 40 pour le reste. J'aurais dû choisir environ 386 fois. Mon commentaire était basé sur mon observation.
À peu près sûr que cela ne vous donnera pas la distribution attendue. Considérons 3 lignes, de poids 80, 10 et 10. Nous nous attendons à ce que la première rangée soit cueillie 80% du temps et les autres avec une probabilité égale, les autres 20% du temps. Si RAND () * 80> 10, nous devons sélectionner la première ligne. Si RAND () * 80 est également réparti entre [0, 80], les chances de dépassement 10 sont 69/81, soit 85%. Ce sera surreprésenté. Même si je faisais quelques erreurs hors-tête ici.
Comme Daniel dit, cela ne donne pas la distribution attendue
@DanielPapaSien "Rand () * 80> 10" Je ne sais pas comment cela est pertinent pour la requête dans la réponse, qui recherche le plus haut des 3 valeurs aléatoires pondérées. La réponse a l'air intuitivement correcte, mais je ne dis pas que c'est. Je ne vois tout simplement pas comment votre raisonnement le réfute. (Aussi, rand () code> doit être un point flottant pour que cela soit pleinement précis, il est donc dès de 70 et 80 à 69 et 81, dans la mesure où cela compte ici.)
Oui, ma réponse regarde i> intuitivement correcte, mais @DanielPapaSian a raison. Les distributions produites par cette méthode ne sont pas correctes. Merci pour les commentaires. J'ai modifié la réponse en conséquence, et si vous pouvez voir comment améliorer l'approche ci-dessus, n'hésitez pas à modifier ma réponse plus loin!
Cela fonctionne assez bien pour un petit nombre de lignes, mais s'il y a 1000 rangées poids 100 et 1000 avec poids 99, les 99s verront très peu d'action. Je pense que l'utilisation d'une distribution aléatoire uniforme est le problème.
J'ai essayé la solution de Van et, bien que cela fonctionne, ce n'est pas rapide.
La manière dont je résolvez ce problème est de maintenir une table distincte et liée pour la pondération . La structure de la table de base est similaire à celle-ci: p> si j'ai un enregistrement dans sur un jeu de données avec 976 enregistrements dans une version de la solution de VAN P>
Joignant à une table secondaire pour la pondération p> li>
OL> Table1 code> avec un poids de 3, puis je crée 3 enregistrements dans Tablef_weight Code>, lié à Table1 Code> via le champ TABLE1_ID CODE>. Quelle que soit la valeur de de poids code> dans table1 code>, c'est combien d'enregistrements liés je crée dans table1_weight code>. P> Test H3>
Table1 code> avec un poids total de 2031 et donc 2031 enregistrements dans Table1_weight code>, j'ai exécuté les deux SQLS suivants: P >
SELECT t.*
FROM table1 t
INNER JOIN table1_weight w
ON w.table1_id = t.id
ORDER BY RAND()
LIMIT 1
Avec l'inconvénient principal étant la taille de la table pour les grandes tables :)
ou pour des poids élevés ... et aucun support pour les poids fractionnaires.
Celui-ci semble fonctionner, mais je ne suis pas sûr des maths derrière cela.
jasen=# with g as ( select generate_series(1,209000) as i )
,r as (select ( select t.weight as w
FROM t
WHERE t.weight > 0
ORDER BY ( random() / t.weight ) + (g.i*0) LIMIT 1 ) from g)
select r.w, count(*), r.w*1000 as expect from r group by r.w;
w | count | expect
-----+-------+--------
99 | 98978 | 99000
10 | 10070 | 10000
100 | 99952 | 100000
(3 rows)
Je pense que le plus simple est en réalité d'utiliser l'échantillonnage des réservoirs pondérés: C'est une excellente méthode qui vous permet de choisir M des éléments de n où la probabilité d'être choisie pour chaque élément est proportionnelle à son poids. Cela fonctionne aussi bien lorsque vous ne voulez qu'un élément.
La méthode est décrite dans Cet article . Notez qu'ils choisissent les plus grandes valeurs de POW (Rand (), 1 / Poids), ce qui équivaut à choisir les plus petites valeurs de -log (rand ()) / poids. P> P>
C'est une réponse merveilleuse! Merci! Il suffit d'ajouter mes deux cents: ne serait-il pas plus élégant d'écrire un journal (1-rand ()) pour éviter le journal (0) car les valeurs aléatoires sont probablement dans [0,1 [(non cochées)?
Cela ressemble à une bonne méthode, mais la distribution peut être très asymétrique. J'ai essayé des poids pour plusieurs rangées où tous les poids étaient soit 67 ou 33 (c'est-à-dire environ 2/3 ou 1/3) et dans mon cas, toutes les lignes choisies avaient le poids plus élevé. Pas certain de pourquoi.
Voir cette question: Stackoverflow.com/questions/ 58457 / ...