2020-04-03 18:10:42
围观(3728)
很多 APP / 小程序 为了增强用户交互或者激励用户都会开发一套抽奖.
博主最近就遇到了这样的抽奖需求.
设置多个奖品 如:
1 2 3 4 5 6 7 8 | A奖品 中奖概率百分之十 总量十个 B奖品 中奖概率百分之五 总量五个 C奖品 中奖概率百分之十五 总量十五个 D奖品 中奖概率百分之十 总量十个 F奖品 中奖概率百分之五 总量五个 G奖品 中奖概率百分之二十 总量二十个 H奖品 中奖概率百分之五 总量五个 I奖品 中奖概率百分之五 总量五个 |
将这些奖品信息写入数组:
1 2 3 4 5 6 7 8 9 10 | $list_awards = [ [ 'id' => 1, 'name' => '奖品1' , 'probability' => 10, 'count' => 10], [ 'id' => 2, 'name' => '奖品2' , 'probability' => 5, 'count' => 5], [ 'id' => 3, 'name' => '奖品3' , 'probability' => 15, 'count' => 15], [ 'id' => 4, 'name' => '奖品4' , 'probability' => 10, 'count' => 10], [ 'id' => 5, 'name' => '奖品5' , 'probability' => 5, 'count' => 5], [ 'id' => 6, 'name' => '奖品6' , 'probability' => 20, 'count' => 20], [ 'id' => 7, 'name' => '奖品7' , 'probability' => 5, 'count' => 5], [ 'id' => 8, 'name' => '奖品8' , 'probability' => 5, 'count' => 5], ]; |
直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | $map_list_awards = []; foreach ( $list_awards as $key => $value ) { $map_list_awards [ $value [ 'id' ]] = $value ; } // 存放奖品 $new_list_awards = []; foreach ( $map_list_awards as $row_awards ) { // 判断余量 / 总量 当余量大于等于一时 即奖池中有足够的库存才进行抽奖 if ( $row_awards [ 'count' ] >= 1) { for ( $i = 1; $i <= $row_awards [ 'probability' ]; $i ++) { // 按照概率存入数组 比如奖品中奖改成为五 则会入库五个 $new_list_awards [] = $row_awards ; } } } // 计算当前剩余的百分比 $probability = 100 - array_sum (array_column( $list_awards , 'probability' )); // 将剩下的百分比 作为空数组存入 for ( $i = 0; $i < $probability ; $i ++) { $new_list_awards [] = []; } // 生成随机数 因为数组是从索引 0 开始 所以长度一百的数组 索引只到 99 $select_index = mt_rand(0, 99); if ( empty ( $new_list_awards [ $select_index ])) { // 如果是空数组 自然没中奖 echo "谢谢惠顾" ; } else { // 存入文件 更加方便查看中奖记录, 也可以存入数据库 $file = fopen ( 'test.log' , 'a+' ); fwrite( $file , '奖品ID:' . $new_list_awards [ $select_index ][ 'id' ] . '; 抽第' . $o . '次时中奖; 奖品几率' . $new_list_awards [ $select_index ][ 'probability' ] . '; 奖品剩余:' . $new_list_awards [ $select_index ][ 'count' ] . " "); fclose( $file ); echo '恭喜中奖了, 奖品:' . $new_list_awards [ $select_index ][ 'name' ] . " "; // 中奖了当然要减少奖品余量 $map_list_awards [ $new_list_awards [ $select_index ][ 'id' ]][ 'count' ]--; } |
这个抽奖原理就是, 每个奖品设置中奖率, 全部奖品中奖率加起来是等于 100 (百分百), 根据这个特性, 创建了一个长度为 100 的数组, 比如设置的奖品总和几率是 70, 那么这个数组 1 到 70 的索引都有对应奖品的值, 然后在 1 到 100 之间生成随机数, 如果随机数是在 1 - 70 之间, 说明中奖了. 当然 中奖率还可以再调.
代码写好了, 马上进行测试, 使用 PHP CLI 执行文件测试, 测试条件: 抽奖十万次.
跑完后打开存储记录的文件, 显示信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 奖品ID:3; 抽第0次时中奖; 奖品几率15; 奖品剩余:15 奖品ID:6; 抽第1次时中奖; 奖品几率20; 奖品剩余:20 奖品ID:6; 抽第2次时中奖; 奖品几率20; 奖品剩余:19 奖品ID:6; 抽第3次时中奖; 奖品几率20; 奖品剩余:18 奖品ID:1; 抽第4次时中奖; 奖品几率10; 奖品剩余:10 奖品ID:6; 抽第5次时中奖; 奖品几率20; 奖品剩余:17 奖品ID:4; 抽第6次时中奖; 奖品几率10; 奖品剩余:10 奖品ID:7; 抽第7次时中奖; 奖品几率5; 奖品剩余:5 奖品ID:5; 抽第8次时中奖; 奖品几率5; 奖品剩余:5 奖品ID:6; 抽第9次时中奖; 奖品几率20; 奖品剩余:16 奖品ID:6; 抽第10次时中奖; 奖品几率20; 奖品剩余:15 奖品ID:6; 抽第11次时中奖; 奖品几率20; 奖品剩余:14 奖品ID:2; 抽第13次时中奖; 奖品几率5; 奖品剩余:5 奖品ID:1; 抽第14次时中奖; 奖品几率10; 奖品剩余:9 奖品ID:3; 抽第16次时中奖; 奖品几率15; 奖品剩余:14 奖品ID:1; 抽第17次时中奖; 奖品几率10; 奖品剩余:8 奖品ID:4; 抽第19次时中奖; 奖品几率10; 奖品剩余:9 奖品ID:2; 抽第20次时中奖; 奖品几率5; 奖品剩余:4 奖品ID:6; 抽第21次时中奖; 奖品几率20; 奖品剩余:13 奖品ID:5; 抽第22次时中奖; 奖品几率5; 奖品剩余:4 奖品ID:6; 抽第23次时中奖; 奖品几率20; 奖品剩余:12 奖品ID:1; 抽第24次时中奖; 奖品几率10; 奖品剩余:7 奖品ID:7; 抽第25次时中奖; 奖品几率5; 奖品剩余:4 奖品ID:1; 抽第26次时中奖; 奖品几率10; 奖品剩余:6 奖品ID:3; 抽第28次时中奖; 奖品几率15; 奖品剩余:13 奖品ID:3; 抽第30次时中奖; 奖品几率15; 奖品剩余:12 奖品ID:4; 抽第31次时中奖; 奖品几率10; 奖品剩余:8 奖品ID:2; 抽第33次时中奖; 奖品几率5; 奖品剩余:3 奖品ID:6; 抽第35次时中奖; 奖品几率20; 奖品剩余:11 奖品ID:3; 抽第37次时中奖; 奖品几率15; 奖品剩余:11 奖品ID:3; 抽第39次时中奖; 奖品几率15; 奖品剩余:10 奖品ID:5; 抽第40次时中奖; 奖品几率5; 奖品剩余:3 奖品ID:1; 抽第41次时中奖; 奖品几率10; 奖品剩余:5 奖品ID:3; 抽第42次时中奖; 奖品几率15; 奖品剩余:9 奖品ID:3; 抽第44次时中奖; 奖品几率15; 奖品剩余:8 奖品ID:6; 抽第45次时中奖; 奖品几率20; 奖品剩余:10 奖品ID:6; 抽第46次时中奖; 奖品几率20; 奖品剩余:9 奖品ID:3; 抽第47次时中奖; 奖品几率15; 奖品剩余:7 奖品ID:4; 抽第49次时中奖; 奖品几率10; 奖品剩余:7 奖品ID:6; 抽第50次时中奖; 奖品几率20; 奖品剩余:8 奖品ID:3; 抽第51次时中奖; 奖品几率15; 奖品剩余:6 奖品ID:4; 抽第52次时中奖; 奖品几率10; 奖品剩余:6 奖品ID:7; 抽第54次时中奖; 奖品几率5; 奖品剩余:3 奖品ID:2; 抽第56次时中奖; 奖品几率5; 奖品剩余:2 奖品ID:2; 抽第58次时中奖; 奖品几率5; 奖品剩余:1 奖品ID:4; 抽第59次时中奖; 奖品几率10; 奖品剩余:5 奖品ID:4; 抽第60次时中奖; 奖品几率10; 奖品剩余:4 奖品ID:3; 抽第61次时中奖; 奖品几率15; 奖品剩余:5 奖品ID:6; 抽第62次时中奖; 奖品几率20; 奖品剩余:7 奖品ID:7; 抽第63次时中奖; 奖品几率5; 奖品剩余:2 奖品ID:6; 抽第64次时中奖; 奖品几率20; 奖品剩余:6 奖品ID:8; 抽第65次时中奖; 奖品几率5; 奖品剩余:5 奖品ID:6; 抽第66次时中奖; 奖品几率20; 奖品剩余:5 奖品ID:6; 抽第67次时中奖; 奖品几率20; 奖品剩余:4 奖品ID:7; 抽第68次时中奖; 奖品几率5; 奖品剩余:1 奖品ID:5; 抽第69次时中奖; 奖品几率5; 奖品剩余:2 奖品ID:3; 抽第71次时中奖; 奖品几率15; 奖品剩余:4 奖品ID:5; 抽第72次时中奖; 奖品几率5; 奖品剩余:1 奖品ID:1; 抽第73次时中奖; 奖品几率10; 奖品剩余:4 奖品ID:4; 抽第74次时中奖; 奖品几率10; 奖品剩余:3 奖品ID:3; 抽第75次时中奖; 奖品几率15; 奖品剩余:3 奖品ID:1; 抽第77次时中奖; 奖品几率10; 奖品剩余:3 奖品ID:3; 抽第78次时中奖; 奖品几率15; 奖品剩余:2 奖品ID:4; 抽第79次时中奖; 奖品几率10; 奖品剩余:2 奖品ID:1; 抽第81次时中奖; 奖品几率10; 奖品剩余:2 奖品ID:8; 抽第82次时中奖; 奖品几率5; 奖品剩余:4 奖品ID:6; 抽第83次时中奖; 奖品几率20; 奖品剩余:3 奖品ID:3; 抽第84次时中奖; 奖品几率15; 奖品剩余:1 奖品ID:4; 抽第86次时中奖; 奖品几率10; 奖品剩余:1 奖品ID:1; 抽第88次时中奖; 奖品几率10; 奖品剩余:1 奖品ID:6; 抽第92次时中奖; 奖品几率20; 奖品剩余:2 奖品ID:8; 抽第104次时中奖; 奖品几率5; 奖品剩余:3 奖品ID:6; 抽第113次时中奖; 奖品几率20; 奖品剩余:1 奖品ID:8; 抽第154次时中奖; 奖品几率5; 奖品剩余:2 奖品ID:8; 抽第158次时中奖; 奖品几率5; 奖品剩余:1 |
此时抽奖程序是正常的, 但是有个问题, 刚才测试抽的次数是 10W 次, 但是看记录 到158次 其实全部奖品都已经被抽完了. 第 158 次往后, 就只能全部都是谢谢惠顾了. 可以说是先到先得...
由于对后面进来的抽奖者来说并不公平, 解决方法就是, 将概率减小. 比如上面的代码存的数组长度只有 100 可以增大这个数组长度 就实现了减小记录.
如(直接贴完整代码 代码带循环十万次):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | // 打开文件 准备好存储记录 因为是十万次抽奖测试 所以放在循环体外面 只打开一次 后面循环体内仅写入就好 $file = fopen ( 'test.log' , 'a+' ); $list_awards = [ [ 'id' => 1, 'name' => '奖品1' , 'probability' => 10, 'count' => 10], [ 'id' => 2, 'name' => '奖品2' , 'probability' => 5, 'count' => 5], [ 'id' => 3, 'name' => '奖品3' , 'probability' => 15, 'count' => 15], [ 'id' => 4, 'name' => '奖品4' , 'probability' => 10, 'count' => 10], [ 'id' => 5, 'name' => '奖品5' , 'probability' => 5, 'count' => 5], [ 'id' => 6, 'name' => '奖品6' , 'probability' => 20, 'count' => 20], [ 'id' => 7, 'name' => '奖品7' , 'probability' => 5, 'count' => 5], [ 'id' => 8, 'name' => '奖品8' , 'probability' => 5, 'count' => 5], ]; // Map 处理 $map_list_awards = []; foreach ( $list_awards as $key => $value ) { $map_list_awards [ $value [ 'id' ]] = $value ; } for ( $o = 1; $o <= 100000; $o ++) { $new_list_awards = []; // 将奖品项全部放入新数组 foreach ( $map_list_awards as $row_awards ) { if ( $row_awards [ 'count' ] >= 1) { for ( $i = 1; $i <= $row_awards [ 'probability' ]; $i ++) { $new_list_awards [] = $row_awards ; } } } // 这里原本几率是 100 现在乘以 1000 减少几率 让后面的人也能抽奖 当然 中奖就变难了 $probability = (100 * 1000) - array_sum (array_column( $list_awards , 'probability' )); // 存入空数组 只要随机抽到空数组 说明没中奖 for ( $i = 0; $i < $probability ; $i ++) { $new_list_awards [] = []; } $select_index = mt_rand(0, 99999); // 生成随机数 中奖就看这个了 原本仅仅是 99 减少了中奖几率 if ( empty ( $new_list_awards [ $select_index ])) { echo '谢谢惠顾' ; } else { // 减剩余奖品数量 $map_list_awards [ $new_list_awards [ $select_index ][ 'id' ]][ 'count' ] = $map_list_awards [ $new_list_awards [ $select_index ][ 'id' ]][ 'count' ] - 1; fwrite( $file , '奖品ID:' . $new_list_awards [ $select_index ][ 'id' ] . '; 抽第' . $o . '次时中奖; 奖品几率' . $new_list_awards [ $select_index ][ 'probability' ] . '; 奖品剩余:' . $new_list_awards [ $select_index ][ 'count' ] . " "); echo '中奖了:' . $new_list_awards [ $select_index ][ 'name' ]; } echo '第' . $o . "次 "; } fclose( $file ); |
中奖几率重点就在这两句:
1 2 3 | $probability = (100 * 1000) - array_sum (array_column( $list_awards , 'probability' )); $select_index = mt_rand(0, 99999); |
当然 这个数字越大, 抽奖所需的服务器性能要求也会提高. 一般建议 设置成 100 * 100 然后索引值最大就是 9999.
以这个几率配置博主跑了两次抽奖十万次的, 第一次是第 14252 次就把所有奖品抽完了, 第二次是第 14484 次就把全部奖品抽完了. 两次都是 1.4W 次左右, 说明还是比较稳的, 而且这一共才 75 个奖品(八个奖项的数量加起来), 如果一人抽一次 需要 1.5W 人才能把这奖品抽完, 中奖几率控制在这个数值还是很难中奖的.
实际项目中的效果:
本文地址 : bubaijun.com/page.php?id=171
版权声明 : 未经允许禁止转载!
上一篇文章: 商城或外卖购物车功能开发
下一篇文章: PHP处理字符串隐私保护