想了想,自己写写php版的微信红包随机生成算法,能不能实现类似的功能。//
$bonus_total
红包总金额// $bonus_count 红包个数//
$bonus_type
红包类型 1=拼手气红包 0=普通红包

最近在研究发红包的功能,于是写了个红包的生成算法。

1.1. 源代码

突发奇想给校友微信群发了红包,我设定红包总额为10元,支持28个人随机领取。于是一个有趣的结果出现了:A
领取了 0.26元B 领取了 0.29元C 领取了 0.02元D 领取了 0.56元E 领取了
0.64元……
微信是采用什么样的算法做到的?简单百度了下,目前尚未有官方的说明,仅仅在知乎里有一个较为热门的讨论,链接戳这里,不过他们讨论的太过于深入,有掉坑之嫌。我按照自己的逻辑尝试了下,这个算法需要满足以下几点要求:1、每个人都要能够领取到红包;2、每个人领取到的红包金额总和=总金额;3、每个人领取到的红包金额不等,但也不能差的太离谱,不然就没趣味;4、算法一定要简单,不然对不起腾讯这个招牌;正式编码之前,先搭建一个递进的模型来分析规律设定总金额为10元,有N个人随机领取:

本文实例讲述了php实现的微信红包算法。分享给大家供大家参考。具体如下:

function randBonus($bonus_total=0, $bonus_count=3, $bonus_type=1){ $bonus_items = array(); // 将要瓜分的结果 $bonus_balance = $bonus_total; // 每次分完之后的余额 $bonus_avg = number_format($bonus_total/$bonus_count, 2); // 平均每个红包多少钱 $i = 0; while{ if{ $rand = $bonus_type?(rand(1, $bonus_balance*100-1)/100):$bonus_avg; // 根据红包类型计算当前红包的金额 $bonus_items[] = $rand; $bonus_balance -= $rand; }else{ $bonus_items[] = $bonus_balance; // 最后一个红包直接承包最后所有的金额,保证发出的总金额正确 } $i++; } return $bonus_items;}

红包生成算法的需求预先生成所有的红包还是一个请求随机生成一个红包简单来说,就是把一个大整数m分解分解成n个小整数的过程,小整数的范围是[min,
max]。最简单的思路,先保底,每个小红包保证有min,然后每个请求都随机生成一个0到范围的整数,再加上min就是红包的钱数。这个算法虽然简单,但是有一个弊端:最后生成的红包可能都是min钱数的。也就是说可能最后的红包都是0.01元的。另一种方式是预先生成所有红包,这样就比较容易控制了。我选择的是预先生成所有的红包。

';print_r;?>

N=1 则红包金额=X元;

wxhb_inited = false; $this->apiclient_cert = getcwd()."/apiclient_cert.pem"; $this->apiclient_key = getcwd()."/apiclient_key.pem"; } public function err(){ return $this->error; } public function error(){ return $this->err(); } /** * WXHongBao::newhb() * 构造新红包 * @param mixed $toOpenId * @param mixed $amount 金额分 * @return void */ public function newhb{ if{ $this->error = "金额参数错误"; return; }elseif{ $this->error = "金额太小"; return; }elseif{ $this->error = "金额太大"; return; } $this->gen_nonce_str();//构造随机字串 $this->gen_mch_billno();//构造订单号 $this->setOpenId; $this->setAmount; $this->wxhb_inited = true; //标记微信红包已经初始化完毕可以发送 //每次new 都要将分享的内容给清空掉,否则会出现残余被引用 $this->share_content= ""; $this->share_imgurl = ""; $this->share_url = ""; } /** * WXHongBao::send() * 发出红包 * 构造签名 * 注意第二参数,单发时不要改动! * @return boolean $success */ public function send($url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack",$total_num = 1){ if{ $this->error .= ""; return false; //未初始化完成 } $this->total_num = $total_num; $this->gen_Sign(); //生成签名 //构造提交的数据 $xml = $this->genXMLParam(); //debug file_put_contents; //提交xml,curl //$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"; $ch = curl_init(); curl_setopt($ch,CURLOPT_TIMEOUT,10); curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1); curl_setopt; curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false); curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT,$this->apiclient_cert); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY,$this->apiclient_key); /* if >= 1 ){ curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader); } */ curl_setopt; curl_setopt($ch,CURLOPT_POSTFIELDS,$xml); $data = curl_exec{ curl_close; $rsxml = simplexml_load_string; if($rsxml->return_code == 'SUCCESS' ){ return true; }else{ $this->error = $rsxml->return_msg; return false; } }else{ $this->error = curl_errno; return false; } } /** * WXHongBao::sendGroup() * 发送裂变红包,参数为裂变数量 * @param integer $num 3-20 * @return */ public function sendGroup{ $this->amt_type = "ALL_RAND";//$amt; 固定值。发送裂变红包组文档指定参数,随机 return $this->send($this->api_hb_group,$num); } public function getApiSingle(){ return $this->api_hb_single; } public function getApiGroup(){ return $this->api_hb_group; } public function setNickName{ $this->nick_name = $nick; } public function setSendName{ $this->send_name = $name; } public function setWishing{ $this->wishing = $wishing; } /** * WXHongBao::setActName() * 活动名称 * @param mixed $act * @return void */ public function setActName{ $this->act_name = $act; } public function setRemark{ $this->remark = $remark; } public function setOpenId{ $this->re_openid = $openid; } /** * WXHongBao::setAmount() * 设置红包金额 * 文档有两处冲突描述 * 一处指金额 >=1  * 另一处指金额 >=100 < 20000 [1-200元] * 有待测试验证! * @param mixed $price 单位 分 * @return void */ public function setAmount{ $this->total_amount = $price; $this->min_value = $price; $this->max_value = $price; } //以下方法,为设置分裂红包时使用 public function setHBminmax{ $this->min_value = $min; $this->max_value = $max; } public function setShare($img="",$url="",$content=""){ //https://mmbiz.qlogo.cn/mmbiz/MS1jaDO92Ep4qNo9eV0rnItptyBrzUhJqT8oxSsCofdxibnNWMJiabaqgLPkDaEJmia6fqTXAXulKBa9NLfxYMwYA/0?wx_fmt=png //http://mp.weixin.qq.com/s?__biz=MzA5Njg4NTk3MA==&mid=206257621&idx=1&sn=56241da30e384e40771065051e4aa6a8#rd $this->share_content = $content; $this->share_imgurl = $img; $this->share_url = $url; } private function gen_nonce_str(){ $this->nonce_str = strtoupper); //确保不重复而已 } private function gen_Sign; //其实应该用key重排一次 right? $param["act_name"]=$this->act_name;// if{ //这些是裂变红包用不上的参数,会导致签名错误 $param["client_ip"]=$this->client_ip; $param["max_value"]=$this->max_value; $param["min_value"]=$this->min_value; $param["nick_name"]=$this->nick_name; } $param["mch_billno"] = $this->mch_billno; // $param["mch_id"]=$this->mch_id;// $param["nonce_str"]=$this->nonce_str; // $param["re_openid"]=$this->re_openid;// $param["remark"]=$this->remark; // $param["send_name"]=$this->send_name;// $param["total_amount"]=$this->total_amount;// $param["total_num"]=$this->total_num; // $param["wishing"]=$this->wishing;// $param["wxappid"]=$this->wxappid;// if $param["share_content"] = $this->share_content; if $param["share_imgurl"] = $this->share_imgurl; if $param["share_url"] = $this->share_url; if $param["amt_type"] = $this->amt_type; // ksort; //按照键名排序...艹,上面排了我好久 //$sign_raw = http_build_query."&key=".$this->apikey; $sign_raw = ""; foreach{ $sign_raw .= $k."=".$v."&"; } $sign_raw .= "key=".$this->apikey; //file_put_contents;//debug $this->sign = strtoupper; } /** * WXHongBao::genXMLParam() * 生成post的参数xml数据包 * 注意生成之前各项值要生成,尤其是Sign * @return $xml */ public function genXMLParam(){ $xml = " ".$this->sign." ".$this->mch_billno." ".$this->mch_id." ".$this->wxappid." nick_name."]]> send_name."]]> ".$this->re_openid." ".$this->total_amount." ".$this->min_value." ".$this->max_value." ".$this->total_num." wishing."]]> client_ip."]]> act_name."]]> remark."]]> ".$this->nonce_str." "; if $xml .= "share_content."]]> "; if $xml .= "share_imgurl."]]> "; if $xml .= "share_url."]]> "; if $xml .= "amt_type."]]> "; $xml .=""; return $xml; } /** * WXHongBao::gen_mch_billno 组成: mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入, 如出现超时可再调用。 * @return void */ private function gen_mch_billno(){ //生成一个长度10,的阿拉伯数字随机字符串 $rnd_num = array('0','1','2','3','4','5','6','7','8','9'); $rndstr = ""; while{ $rndstr .= $rnd_num[array_rand]; } $this->mch_billno = $this->mch_id.date.$rndstr; }} ?>然后实例化class. $toOpenId = 'asdasdasd'; //接收红包的用户的微信OpenId $hb = new WXHongBao(); $hb->newhb; //新建一个10元的红包,第二参数单位是 分,注意取值范围 1-200元 //以下若干项可选操作,不指定则使用class脚本顶部的预设值 $hb->setNickName; $hb->setSendName; $hb->setWishing; $hb->setActName; $hb->setRemark; //发送红包 if{ //发送错误 echo $hb->err(); }else{ echo "红包发送成功"; }

好吧,我们现在来体验一下

理想的红包生成算法理想的红包生成结果是平均值附近的红包比较多,大红包和小红包的数量比较少。可以想像下,生成红包的数量的分布有点像正态分布。

1.2. 代码图

N=2
为保证第二个红包可以正常发出,第一个红包金额=0.01至9.99之间的某个随机数
第二个红包=10-第一个红包金额;

希望本文所述对大家的php程序设计有所帮助。

// 发3个拼手气红包,总金额是100元$bonus_items = randBonus;// 查看生成的红包var_dump;// 校验总金额是不是正确,看看微信有没有坑我们的钱var_dump(array_sum;

那么如何实现这种平均线附近值比较多的要求呢?就是要找到一种算法,可以提高平均值附近的概率。那么利用一种”膨胀“再”收缩“的方式来达到这种效果。先平方,再生成平方范围内的随机数,再开方,那么概率就不再是平均的了。具体算法:(设置的总钱数,总人数,最大值,最小值要合理)Php代码

总结

N=3 红包1=0.01至0.98之间的某个随机数 红包2=0.01至的某个随机数
红包3=10-红包1-红包2

另一个使用数组实现的版本,原理差不多:

/** * 求一个数的平方 * @param $n */ function sqr{ return $n*$n; } /** * 生产min和max之间的随机数,但是概率不是平均的,从min到max方向概率逐渐加大。 * 先平方,然后产生一个平方值范围内的随机数,再开方,这样就产生了一种“膨胀”再“收缩”的效果。 */ function xRandom($bonus_min,$bonus_max){ $sqr = intval(sqr($bonus_max-$bonus_min)); $rand_num = rand; return intval; } /** * * @param $bonus_total 红包总额 * @param $bonus_count 红包个数 * @param $bonus_max 每个小红包的最大额 * @param $bonus_min 每个小红包的最小额 * @return 存放生成的每个小红包的值的一维数组 */ function getBonus($bonus_total, $bonus_count, $bonus_max, $bonus_min) { $result = array(); $average = $bonus_total / $bonus_count; $a = $average - $bonus_min; $b = $bonus_max - $bonus_min; // //这样的随机数的概率实际改变了,产生大数的可能性要比产生小数的概率要小。 //这样就实现了大部分红包的值在平均数附近。大红包和小红包比较少。 $range1 = sqr($average - $bonus_min); $range2 = sqr($bonus_max - $average); for ($i = 0; $i < $bonus_count; $i++) { //因为小红包的数量通常是要比大红包的数量要多的,因为这里的概率要调换过来。 //当随机数>平均值,则产生小红包 //当随机数 $average) { // 在平均线上减钱 $temp = $bonus_min + xRandom; $result[$i] = $temp; $bonus_total -= $temp; } else { // 在平均线上加钱 $temp = $bonus_max - xRandom; $result[$i] = $temp; $bonus_total -= $temp; } } // 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。 while  { for ($i = 0; $i < $bonus_count; $i++) { if ($bonus_total > 0 && $result[$i] < $bonus_max) { $result[$i]++; $bonus_total--; } } } // 如果钱是负数了,还得从已生成的小红包中抽取回来 while  { for ($i = 0; $i < $bonus_count; $i++) { if ($bonus_total < 0 && $result[$i] > $bonus_min) { $result[$i]--; $bonus_total++; } } } return $result; } $bonus_total = 200; $bonus_count = 100; $bonus_max = 10;//此算法要求设置的最大值要大于平均值 $bonus_min = 1; $result_bonus = getBonus($bonus_total, $bonus_count, $bonus_max, $bonus_min); $total_money = 0; $arr = array(); foreach ($result_bonus as $key => $value) { $total_money += $value; if{ $arr[$value] += 1; }else{ $arr[$value] = 1; } } //输出总钱数,查看是否与设置的总数相同 echo $total_money; //输出所有随机红包值 var_dump; //统计每个钱数的红包数量,检查是否接近正态分布 ksort;

以上所述是小编给大家介绍的php生成微信红包数组的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

……至此,规律出现啦!开始编码!Php代码

function sendRandBonus($total=0, $count=3, $type=1){ if{ $input = range; if{ $rand_keys =  array_rand; $last = 0; foreach($rand_keys as $i=>$key){ $current = $input[$key]-$last; $items[] = $current; $last = $input[$key]; } } $items[] = $total-array_sum; }else{ $avg = number_format; $i = 0; while{ $items[] = $i以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

header("Content-Type: text/html;charset=utf-8");//输出不乱码,你懂的 $total=10;//红包总额 $num=8;// 分成8个红包,支持8人随机领取 $min=0.01;//每个人最少能收到0.01元 for  { $safe_total=$total-*$min;//随机安全上限 $money=mt_rand($min*100,$safe_total*100)/100; $total=$total-$money; echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元 
'; } echo '第'.$num.'个红包:'.$total.' 元,余额:0 元'; 

输入一看,波动太大,这数据太无趣了!第1个红包:7.48 元,余额:2.52 元
第2个红包:1.9 元,余额:0.62 元 第3个红包:0.49 元,余额:0.13 元
第4个红包:0.04 元,余额:0.09 元 第5个红包:0.03 元,余额:0.06 元
第6个红包:0.03 元,余额:0.03 元 第7个红包:0.01 元,余额:0.02 元
第8个红包:0.02 元,余额:0 元

改良一下,将平均值作为随机安全上限来控制波动差Php代码

header("Content-Type: text/html;charset=utf-8");//输出不乱码,你懂的 $total=10;//红包总额 $num=8;// 分成8个红包,支持8人随机领取 $min=0.01;//每个人最少能收到0.01元 for  { $safe_total=*$min)/;//随机安全上限 $money=mt_rand($min*100,$safe_total*100)/100; $total=$total-$money; echo '第'.$i.'个红包:'.$money.' 元,余额:'.$total.' 元 
'; } echo '第'.$num.'个红包:'.$total.' 元,余额:0 元'; 

输出结果见下图第1个红包:0.06 元,余额:9.94 元 第2个红包:1.55
元,余额:8.39 元 第3个红包:0.25 元,余额:8.14 元 第4个红包:0.98
元,余额:7.16 元 第5个红包:1.88 元,余额:5.28 元 第6个红包:1.92
元,余额:3.36 元 第7个红包:2.98 元,余额:0.38 元 第8个红包:0.38
元,余额:0 元

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图