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(除)等

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM