我即将创建"彩票系统"
看看我的桌子:
userid-lottaryid-amount
1 -------- 1 ---- 1
2 -------- 1 ---- 10
3 -------- 1 ---- 15
4 -------- 1 ---- 20
我想选出一个胜利者。第二名是另一个人。
我不能随机选择获胜者,因为第四位用户有20张票,而第一位用户只有一张。所以我需要按权重生成随机结果,以便更公平。
我在下面找到了php函数,但我不知道如何使用它
function weighted_random_simple($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_rand(0, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}
$values = array('1', '10', '20', '100');
$weights = array(1, 10, 20, 100);
echo weighted_random_simple($values, $weights);
我必须将userid
colomn作为数组获取给$values
,并将amount
colomn获取给$weights
。但我做不到。
这是我到目前为止的代码:
$query = $handler->prepare("SELECT
`cvu`.`lottaryid` as `lottaryid`,
`cvu`.`userid` as `userid`,
`cvu`.`amount` as `amount`,
`members`.`id` as `members_memberid`,
`members`.`username` as `username`
FROM `lottariesandmembers` as `cvu`
LEFT JOIN `members` as `members` ON `cvu`.`userid` = `members`.`id` WHERE `cvu`.`lottaryid` = 2");
$query->bindParam(':lottaryid', $lottaryid, PDO::PARAM_INT);
$query->execute();
while($r = $query->fetch()) {
for ( $count=1 ; $count <= $r["amount"] ; $count++ ) {
$abcprint = "$r[userid].$count - $r[username] - <br>";
echo "$abcprint";
}
}
我有这个代码,只列出了用户数量的多少倍。例如:
1.1 user1
2.1 user2
2.2 user2
2.3 user2
..
2.10 user2
3.1 user3
..
3.15 user3
4.1 user4
..
4.20 user4
等等……但我一直纠结于如何在名单上选出赢家。
如果你愿意帮助我,我想合并这些代码并创建这个小脚本。
如果你从另一方面看到解决方案,我也愿意集思广益。
您可以构建一个大数组,然后从该数组中随机选择一个值,而不是像现在这样打印出值。
while($r = $query->fetch()) {
for ( $i=0; $i <= $r["amount"]; $i++ ) {
// Add the user into the array as many times as they have tickets
$tickets[] = $r['userid'];
}
}
// select the first place winner
$first = $tickets[mt_rand(0, count($tickets) - 1)];
// remove the first place winner from the array
$tickets = array_values(array_filter($tickets, function($x) use ($first) {
return $x != $first;
}));
// select the second place winner
$second = $tickets[mt_rand(0, count($tickets) - 1)];
我相信有一种更有效的方法可以用数学来做这件事,但我需要多考虑一下。。。
这不是很优雅,但应该适用于小型彩票。
它只是构造一个巨大的数组,并随机选择一个元素。
想想有一顶装满纸条的大帽子。每个持有人在"纸条"中获得他们的股份,每个人都标有他们的id。即10张纸条上写着持有人的名字"a",20张纸条上有"b",依此类推…
<?php
$holder_totals = array(
'a' => '10',
'b' => '20',
'c' => '20',
'd' => '50'
);
$big_hat = array();
foreach($holder_totals as $holder_id => $total) {
$holder_hat = array_fill(0, intval($total), $holder_id);
$big_hat = array_merge($big_hat, $holder_hat);
}
// Drum roll
foreach (range(1,4) as $n) {
$random_key = array_rand($big_hat);
printf("Winner %d is %s.'n", $n, $big_hat[$random_key]);
unset($big_hat[$random_key]); // Remove winning slip
}
样本输出:
Winner 1 is d.
Winner 2 is c.
Winner 3 is d.
Winner 4 is b.
大帽子看起来像这样:
Array
(
[0] => a
[1] => a
[2] => a
[3] => a
[4] => a
[5] => a
[6] => a
[7] => a
[8] => a
[9] => a
[10] => b
[11] => b
[12] => b
[13] => b
[14] => b
... and so on...
)
/**
* getRandomWeightedElement()
* Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
* An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
* The return value is the array key, A, B, or C in this case. Note that the values assigned
* do not have to be percentages. The values are simply relative to each other. If one value
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers.
*
* @param array $weightedValues
*/
function getRandomWeightedElement(array $weightedValues) {
$rand = mt_rand(1, (int) array_sum($weightedValues));
foreach ($weightedValues as $key => $value) {
$rand -= $value;
if ($rand <= 0) {
return $key;
}
}
}
这是一个高效而灵活的函数。但是如果你想使用非整数加权,你必须修改它。
您可以使用我的库nspl中的weightedChoice函数。
use function 'nspl'rnd'weightedChoice;
// building your query here
$pairs = [];
while($r = $query->fetch()) {
$pairs[] = [$r['userid'], $r['amount']];
}
$winnerId = weightedChoice($pairs);
您可以使用composer:安装库
composer require ihor/nspl
或者你可以简单地重用GitHub:中的weightedChoice代码
/**
* Returns a random element from a non-empty sequence of items with associated weights
*
* @param array $weightPairs List of pairs [[item, weight], ...]
* @return mixed
*/
function weightedChoice(array $weightPairs)
{
if (!$weightPairs) {
throw new 'InvalidArgumentException('Weight pairs are empty');
}
$total = array_reduce($weightPairs, function($sum, $v) { return $sum + $v[1]; });
$r = mt_rand(1, $total);
reset($weightPairs);
$acc = current($weightPairs)[1];
while ($acc < $r && next($weightPairs)) {
$acc += current($weightPairs)[1];
}
return current($weightPairs)[0];
}