• PHP实现简单发红包(随机分配,平均分配)


    最近碰到一些情况,把思路重新整理了一下,敲出代码。记下来,以后可以借鉴,进一步优化等。

    大致的思路:红包主要分两种,一种是平均分配,一种是随机分配。

    1、平均分配

      平均分配相对好理解,只要把钱平均分给每一个人就可以了

      这里有一个情况,就是钱的总额是固定的,但是分配的人数,不一定可以整除余0,那么剩下的如何分配呢?

      这里,剩余的钱(极少),多分到的人,也就是多分1分钱(在计算处理时,单位是“分”)

      所以,使用的处理办法是,从前到后(谁手快,谁多分,蚊子再小也是肉),逐一分这剩余的钱,每人1分钱,直到钱没为止

    2、随机分配

      我这个随机分配,比较简单,只比平均分配多了一个步骤(此步骤根据需要,可以循环多次使用)。

      先是随机分配的两个特殊情况:

        a.总金额不够所有人分。例如,最小的钱是1分钱,分给10个人。是不可以的

        b.总金额正好只能每人平均分1分钱。例如,0.1元,分给10个人,任何一个人多1分钱,就会有人没钱分

      这两个特例单独处理

      接下来的情况就是,正常的随机分钱,为了尽量让每个人分钱的概率差不多,用了下边的方法

        a.先将钱按当前分钱的人数计算平均值

        b.随机的钱数的取值范围是(1,平均值)

        c.可以分配的总钱数减去生成的随机钱数,得到下一次分配时的可分配总钱数

        d.重复a~c步骤,最终完成随机分配

      按照以上的方法随机分完之后,消耗的总金额是一定小于等于输入的总金额的,那么,在处理完随机分配之后,还要对剩余的金额处理

      这里实现的,就是将剩余的金额,再用平均分配的方式,分散到每一个人的手里

    以上就是实现发红吧的大致思路,下边代码,就是根据这个思路整理而成

    一、rand_money方法,完成一次随机分配

     1 /*
     2  * 随机分钱
     3  * 参数:$money,参与分钱的金额
     4  *    $num,参与分钱的人数
     5  */
     6 function rand_money($money, $num)
     7 {
     8     $arr = [];//结果数组
     9     $money = $money * 100;// 将元转成分(小数计算有误差,随机数也都是整数)
    10     $rest_money = $money;// 初始化,剩余钱的变量
    11     $average = $rest_money / $num;// 求出均分情况下,每人的红包值
    12     
    13     if ($average < 1) {// 钱不够所有人分
    14         return false;
    15     }elseif($average == 1){// 所有人平均分这笔钱(钱数只够这么分的)
    16         for ($i=0; $i<$num; $i++) {
    17             $arr[] = $average;
    18         }
    19     }else{// 每个人随机分配
    20         for ($i=0; $i<$num; $i++) {
    21             $range_money = round(($rest_money / ($num - $i)));
    22             $rand_money = mt_rand(1, $range_money);
    23             $arr[] = $rand_money;
    24             $rest_money = $rest_money - $rand_money;// 获取剩下的钱
    25         }
    26     }
    27     return $arr;
    28 }

     二、average_money方法,既可以自己完成平均分配,又可以协助随机分配,完成剩余金额的分配

     1 function average_money($money, $num, $arr=[], $conversion_val=1)
     2 {
     3     $money = $money * 100;
     4     $arr_sum = 0;//保存数组和
     5     if (count($arr) > 0) {// 随机分配,会调用此方法将剩余的钱分掉,此数组为随机分配后的结果
     6         foreach ($arr as $k=>$v)  {
     7             $arr[$k] = $v * 100 / $conversion_val;// 如果单位有变化这调整一下,一直以分为单位处理数据
     8         }
     9         $arr_sum = array_sum($arr);// 统计随机分配已经分配了总钱数
    10     } else {
    11         for ($i=0; $i<$num; $i++) {
    12             $arr[] = 0;// 初始化每个人的数组,兼容下边循环处理部分
    13         }
    14     }
    15     $add_money = $money - $arr_sum;
    16     // 如果总钱数和之前随机分配的数组的总和差值为0,就说明随机分配已经将钱全部分出去了,就不需要再平均分配处理了
    17     if ($add_money == 0) {
    18         return $arr;
    19     }
    20     // 先把剩余的能均分的部分均分一下,然后若再有剩余,则从前到后,注意分配
    21     for ($i = 0; $i < $num; $i++) {
    22         $arr[$i] = $arr[$i] + floor($add_money / $num);// 如果之前有随机分配,则是将剩余的钱平均追加入随机分配的值里
    23     }
    24     $arr_sum = array_sum($arr);// 分配后,求和,用于修正最后剩余的零钱
    25     // 如果还有剩余,这部分说明每人一分都不够,就从头开始没人一分的分下去,直到分完为止
    26     $odd_money = bcsub($money, $arr_sum, 0);// 针对钱的计算,建议使用bc函数,普通的计算方法有误差
    27     $i = 0;
    28     while ($odd_money >= 1) {
    29         $arr[$i] = $arr[$i] + 1;// 每人加1分钱
    30         $odd_money = $odd_money - 1;// 剩余的金额,每分掉一个人,就减1分钱
    31         $i++;
    32     }
    33     return $arr;
    34 }

     三、红包调用方法,根据不同类型,返回不同红包的结果

     1 /*
     2  * 红包方法
     3  * 参数:$money,参与分钱的金额
     4  *    $num,参与分钱的人数
     5  *    $type,红包类型,0平均分配,1随机分配
     6  */
     7 function get_red ($money, $num, $type=0) {
     8     if ($type) { // 非0,随机红包
     9         $arr_rand = rand_money($money, $num);// 先随机分配
    10         $arr = average_money($money, $num, $arr_rand, 100);// 再平均分配  
    11     } else { // 平均分配红包
    12         $arr = average_money($money, $num);
    13     }
    14     return $arr;
    15 }

     四、实例代码测试

     1 $a = get_red(66.61, 11, 0);
     2 //将最终结果,转换成元为单位
     3 foreach ($a as $k=>$val) {
     4     $a[$k] = $val / 100;
     5 }
     6 print_r($a);
     7 echo '<br />'.array_sum($a);
     8 
     9 $r = get_red(66.61, 11, 1);
    10 //将最终结果,转换成元为单位
    11 foreach ($r as $k=>$val) {
    12     $r[$k] = $val / 100;
    13 }
    14 echo '<br />';
    15 print_r($r);
    16 echo '<br />'.array_sum($r);

    以上的代码,就完成了红包的操作。

    这代码只是,简单的实现。这其中还有特殊情况,比如,每次随机的数都是最小的数,虽然概率很低。

    那么这种情况,只做一次随机分配,貌似效果并不好。毕竟后边就是平均分配了,这样每一个人的终值非常接近平均值。

    所以,可以考虑,在一次随机分配之后,计算已分配总钱数,根据该总钱数判断是否需要再次进行随机分配,然后将两次或者多次随机分配的值同key合并。

    最后再把剩余的金额“平均分配”后,同key加到一起。这样的结果效果更好。

    注意:

    1、日常人们习惯金钱的单位都是“元”,但这里,尽量转成“分”;小数计算误差大,随机数生成也都是整数

    2、如果可以,金钱在计算时,尽量使用bc高精度函数。如:bcadd(加),bcsub(减),bcmul(乘),bcdiv(除)等

  • 相关阅读:
    SQL 语句优化中间表的使用优化
    SQL 语句优化OR 语句优化案例
    浅谈系统优化设计复杂运算放在逻辑层还是在数据库层?
    linux命令综合
    MySQL常用命令
    PHP知识点积累
    [Git] 生成token解决github remote: Support for password authentication was removed on August 13, 2021.
    [uniapp] GOFLY在线客服系统 uniapp增加播放背景音效或者按钮音效
    [Golang]gorm更新数据update 解决值为0时被忽略
    光阴真的是贱(似箭),一不小心就又过一年了
  • 原文地址:https://www.cnblogs.com/leafinwind/p/10285591.html
Copyright © 2020-2023  润新知