PHP用拋物線的模型實現微信紅包生成算法的程序源碼


<?php

/*
 *Author:Kermit
 *Time:2015-8-26
 *Note:紅包生成隨機算法
 */ 

header("Content-type:text/html;charset=utf-8");
date_default_timezone_set('PRC');

#紅包生成的算法程序
class reward
{
     public $rewardMoney;        #紅包金額、單位元
     public $rewardNum;                  #紅包數量
     public $scatter;            #分散度值1-10000
     public $rewardArray;        #紅包結果集
     
     #初始化紅包類
     public function __construct()
     {
            $this->rewardArray=array();
     }
     
     #執行紅包生成算法
     public function splitReward($rewardMoney,$rewardNum,$scatter=100)
     {
             #傳入紅包金額和數量
             $this->rewardMoney=$rewardMoney;
            $this->rewardNum=$rewardNum;
            $this->scatter=$scatter;
            $this->realscatter=$this->scatter/100;
            
            /*
             *前言:今天我突然這樣一想,比如要把1個紅包分給N個人,實際上就是相當於要得到N個百分比數據
             *     條件是這N個百分比之和=100/100。這N個百分比的平均值是1/N。
             *     並且這N個百分比數據符合一種正態分布(多數值比較靠近平均值)
             *觀點:微信紅包里很多0.01的紅包,我覺得這是微信程序里的人為控制,目的是為了防止總紅包數超過總額,先分了幾個0.01的紅包。
             *     不然不管是以隨機概率還是正態分布都很難會出現非常多的0.01元紅包。
             */
            
            #我的思路:正如上面說的,比如:1個紅包發給5個人,我要得出5個小數,它們的和是1,他們的平均值是1/5
            #計算出發出紅包的平均概率值、精確到小數4位。即上面的1/N值。
            $avgRand=round(1/$this->rewardNum,4);
             
            #紅包的向平均數集中的分布正像數學上的拋物線。拋物線y=ax2,|a|越大則拋物線的開口就越小,|a|越小則拋物線的開口就越大,a>0時開口向上,我們這都是正數,就以a>0來考慮吧。
            #程序里的$scatter值即為上方的a,此值除以100,當做100為基准,
            #通過開方(數學里的拋物線模型,開方可縮小變化值)得出一個小數字較多(小數字多即小紅包多)的隨機分布,據此生成隨機數
            $randArr=array();
            while(count($randArr)<$rewardNum)
            {
                    $t=round(sqrt(mt_rand(1,10000)/$this->realscatter));
                    $randArr[]=$t;
            }
            
            #計算當前生成的隨機數的平均值,保留4位小數
            $randAll=round(array_sum($randArr)/count($randArr),4);
            
            #為將生成的隨機數的平均值變成我們要的1/N,計算一下生成的每個隨機數都需要除以的值。我們可以在最后一個紅包進行單獨處理,所以此處可約等於處理。
            $mixrand=round($randAll/$avgRand,4);
            
            #對每一個隨機數進行處理,並剩以總金額數來得出這個紅包的金額。
            $rewardArr=array();
            foreach($randArr as $key=>$randVal)
            {
                    $randVal=round($randVal/$mixrand,4);
                    $rewardArr[]=round($this->rewardMoney*$randVal,2);
            }
            
            #對比紅包總數的差異、修正最后一個大紅包
            sort($rewardArr);
            $rewardAll=array_sum($rewardArr);
            $rewardArr[$this->rewardNum-1]=$this->rewardMoney-($rewardAll-$rewardArr[$this->rewardNum-1]);
            rsort($rewardArr);

            #對紅包進行排序一下以方便在前台圖示展示
            foreach($rewardArr as $k=>$value)
            {
                    $t=$k%2;
                    if($t) array_push($this->rewardArray,$value);
                    else array_unshift($this->rewardArray,$value);
            }
            $rewardArr=NULL;
            return $this->rewardArray;
     }
    
}

$money=1000;    #總共要發的紅包數;
$people=50;        #總共要發的人數
$scatter=100;    #分散度
$reward=new reward();
$rewardArr=$reward->splitReward($money,$people,$scatter);
echo "發放紅包個數:{$people},紅包總金額{$money}元。下方所有紅包總額之和:".array_sum($reward->rewardArray).'元。下方用圖展示紅包的分布';
echo '<hr>';
echo "<table style='font-size:12px;width:600px;border:1px solid #ccc;text-align:left;'><tr><td>紅包金額</td><td>圖示</td></tr>";
foreach($rewardArr as $val)
{
    #線條長度計算
    $width=intval($people*$val*300/$money);
    echo "<tr><td>{$val}</td><td width='500px;text-align:left;'><hr style='width:{$width}px;height:3px;border:none;border-top:3px double red;margin:0 auto 0 0px;'></td></tr>";    
}
echo "</table>";
?>

 


免責聲明!

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



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