我有一张这样的表:
ID chance
1 1
2 2
3 4
4 1
现在我需要从这个表中选择一个rand()
SELECT * FROM table
ORDER BY RAND()
LIMIT 1
但ID#2与ID#1 AND 4相比,选择的机会增加了一倍。与ID#1 AND 4相比,ID#3的选择机会是其四倍。
与基于偶然性的彩票有点类似。
答案 0 :(得分:2)
这是彩票在某些游戏中的运作方式。给出类似于您的示例的表(例如,我们还有1 + 2 + 4 + 1 = 8
列表示获得此特定奖励的基于值的可能性),算法为:
1..max
)。max
(当前示例中8
为5
)的值。说,我们已经生成了数字0 < 5 <= (0) + 1
。我们的比较步骤是:
1 < 5 <= (1) + 2
是假的,所以ID1不是我们得到的。左边是0,因为我们从0开始计算。1 + 2 < 5 <= (1 + 2) + 4
是假的,所以ID2不是我们得到的。var rewards = [
{ id: 1, chance: 1 },
{ id: 2, chance: 2 },
{ id: 3, chance: 4 },
{ id: 4, chance: 1 }
];
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function generate() {
var sum = 0;
var next_sum = 0;
var random = getRandomInt(1, rewards.reduce(function(pv, cv) {
return pv + cv.chance;
}, 0));
for (var i = 0; i < rewards.length; i++) {
next_sum = sum + rewards[i].chance;
if ((random > sum) && (random <= next_sum)) {
return rewards[i].id;
}
sum += rewards[i].chance;
}
}
var winnerCounts = {}, i, winner;
for (i = 0; i < 8000; i++) {
winner = generate();
winnerCounts[winner] = (winnerCounts[winner] || 0) + 1;
}
console.log("Number of times each id was selected after %d itrations", i);
console.log(winnerCounts);
是真的,所以我们得到了ID3。JavaScript中的示例:
decode-hex-string ( a1 u1 a2 u2 -- a2 u )
&#13;
答案 1 :(得分:2)
以下是SQL Fiddle,仅提供MySQL解决方案
select * from (
select id, @running_total as previous_total, @running_total := @running_total + chance AS running_total, until.rand
from (
select round(rand() * init.max) as rand from (
select sum(chance) - 1 as max from demo
) as init
) as until,
demo,
( select @running_total := 0.00 ) as vars
) as results
where results.rand >= results.previous_total and results.rand < results.running_total
算法如下:
max
[0, max)
previous_total (initially 0)
和current_total
[previous_total, current_total)
因为我们甚至有机会在[0, sum_of_all_chances)
区间内选择每个数字,所以我们可以在这个区间内为每个条目设置尽可能多的数字,因为它有机会被选中,确保均匀分布。
@running_total
只是一个MySQL变量,我只使用( select @running_total := 0.00 ) as vars
作为给它初始值的方法。
另外,我使用(
select round(rand() * init.max) as rand from (
select sum(chance) - 1 as max from demo
) as init
) as until
作为总结机会并存储MySQL rand
函数生成的随机数的方法。希望这使代码易于理解。
答案 2 :(得分:0)
您有一个概率列表: 1 / 8 , 2 / 8 , 4 / 8 和 1 / 8 ,您需要根据此选择项目。解决方案是选择1到8之间的数字,然后在这些范围[1,1] [2,3] [4,7] [8,8]
内查找。
这是SQL查询,它根据机会为每行添加下限和上限:
SELECT
testdata.id,
testdata.chance,
SUM(IFNULL(prevdata.chance, 0)) + 1 AS lb,
SUM(IFNULL(prevdata.chance, 0)) + testdata.chance AS ub
FROM testdata
LEFT JOIN testdata AS prevdata ON testdata.ID > prevdata.ID
GROUP BY testdata.id, testdata.chance
输出:
+------+--------+------+------+
| id | chance | lb | ub |
+------+--------+------+------+
| 1 | 1 | 1 | 1 |
| 2 | 2 | 2 | 3 |
| 3 | 4 | 4 | 7 |
| 4 | 1 | 8 | 8 |
+------+--------+------+------+
其余的是直截了当的。选择介于1和SUM_OF_ALL_CHANCES:
之间的随机数SELECT @total := SUM(chance) FROM testdata;
SELECT @random := FLOOR(RAND() * @total) + 1;
在上表中查找该数字:
SELECT
testdata.id,
testdata.chance,
SUM(IFNULL(prevdata.chance, 0)) + 1 AS lb,
SUM(IFNULL(prevdata.chance, 0)) + testdata.chance AS ub
FROM testdata
LEFT JOIN testdata AS prevdata ON testdata.ID > prevdata.ID
GROUP BY testdata.id, testdata.chance
HAVING @random BETWEEN lb AND ub
答案 3 :(得分:0)
如果你需要清晰的MySQL解决方案,你可以使用它:
SELECT id FROM`table` ORDER BY -LOG(1-RAND())/ chance LIMIT 1
这是关于从指数分布中选择一个随机数 http://www.tushar-mehta.com/publish_train/xl_vba_cases/0806%20generate%20random%20numbers.shtml
简单代码&#34;仅用于测试&#34;
$sql = "SELECT id FROM `table` ORDER BY -LOG(1-RAND())/chance LIMIT 1";
$Res=array();
for ($i=0;$i<10000;$i++) {
$result = mysqli_query($db,$sql);
$row=mysqli_fetch_array($result, MYSQLI_ASSOC);
if (isset($row['id'])) {
echo "$i. => ".($row['id'])."\n";
if (!isset($Res[$row['id']])) $Res[$row['id']]=0;
$Res[$row['id']]++;
} else {
echo ' error.432 ';exit;
}
}
print_r($Res);
你会看到&#34; 2&#34;两倍于#34; 4&#34;或&#34; 1&#34;。并且&#34; 3&#34;比&#34; 2&#34;
多两倍