First, Background introduction
A burst of business there is a need to generate red envelopes, divided into fixed red envelopes and random red envelopes, fixed red envelopes There is nothing to say, random red envelopes required to specify the minimum value, and the maximum value, must have at least one maximum, can have no minimum value, but any red envelopes cannot be less than the minimum value.
Have never done this before, a little bit Meng B, so went to Baidu, the results found to find the red envelope algorithm has a variety of bugs, either will be calculated negative, or exceed the maximum value, so decided to make a set of their own.
Second, the basic ideas
In the field of random number generation, I borrowed the idea of the blogger @ the miserable master:
The original : For example, to give 1 red envelopes to the N-person, is actually equivalent to get n percent data condition is the sum of the N percent of =100/100. The average of these N percentages is 1/n. and this n percent data conforms to a normal distribution (most values are closer to the average).
Interpretation : For example, I have 1000 yuan, hair 50 red envelopes, the first random 50 numbers, and then calculate the mean value of the 50 numbers $avg, with $avg/(1/n), and then get a base $mixrand, and then the random out of the 50 numbers are removed to $ Mixrand, get the percentage of each number relative base $randval, and then use the $randval multiplied by 1000 dollars, you can get each red envelope specific amount.
Still not quite sure? It's okay, let's go with the code!
Third, talk is cheap, show me your code!
red envelope Generation core algorithm:
<?php/* * Author:xx_lufei * time:2016 September 14 09:55:36 * Note: Red envelopes generate random algorithm */class reward{public $rewardMoney; #红包金额, unit public $rewardNum; #红包数量 #执行红包生成算法 Public Function Splitreward ($rewardMoney, $rewardNum, $max, $min) {#传入红包金额和数量, because decimals are calculated during the calculation There is a lot of error, so we directly enlarge the amount of 100 times times, the subsequent calculations are all with integers $min = $min * 100; $max = $max * 100; #预留出一部分钱作为误差补偿, ensure that each red envelope has at least one minimum value $this->rewardmoney = $rewardMoney * 100-$rewardNum * $min; $this->rewardnum = $rewardNum; #计算出发出红包的平均概率值, accurate to decimal 4 bits. $avgRand = 1/$this->rewardnum; $RANDARR = Array (); #定义生成的数据总合sum $sum = 0; $t _count = 0; while ($t _count < $rewardNum) {#随机产出四个区间的额度 $c = rand (1, 100); if ($c <) {$t = round (sqrt (Mt_rand (1, 1500)); } else if ($c < sqrt) {$t = round (Mt_rand (1500, 6500)); } else if ($c <) {$t =Round (sqrt (Mt_rand (6500, 9500)); } else {$t = round (sqrt (Mt_rand (9500, 10000))); } + + $t _count; $sum + = $t; $RANDARR [] = $t; #计算当前生成的随机数的平均值, keep 4 decimal places $randAll = Round ($sum/$rewardNum, 4); #为将生成的随机数的平均值变成我们要的1/n, calculate the total cardinality mixrand for each random number to divide by. This can be treated here, the resulting error will be gathered up #总基数 = mean/average probability $mixrand = round ($randAll/$avgRand, 4); #对每一个随机数进行处理, and multiply the total amount by the amount of this red envelope. $REWARDARR = Array (); foreach ($randArr as $key = + $randVal) {#单个红包所占比例randVal $randVal = round ($randVal/$mixrand, 4 ); #算出单个红包金额 $single = Floor ($this->rewardmoney * $randVal); #小于最小值直接给最小值 if ($single < $min) {$single + = $min; } #大于最大值直接给最大值 if ($single > $max) {$single = $max; } #将红包放入结果数组 $rewardArr [] = $single; } #对比红包总数的Difference, put the difference on the first red envelope $rewardAll = Array_sum ($REWARDARR); $REWARDARR [0] = $rewardMoney *-($REWARDALL-$rewardArr [0]); #此处应使用真正的总金额rewardMoney, $REWARDARR [0] may be less than 0 #第一个红包 Less than 0 o'clock, do correction if ($REWARDARR [0] < 0) {Rsort ($REWARDARR); $this->add ($REWARDARR, $min); } rsort ($REWARDARR); #随机生成的最大值大于指定最大值 if ($rewardArr [0] > $max) {#差额 $diff = 0; foreach ($rewardArr as $k = & $v) {if ($v > $max) {$diff + = $v-$max; $v = $max; } else {break; }} $transfer = Round ($diff/($this->rewardnum-$k + 1)); $this->diff ($diff, $REWARDARR, $max, $min, $transfer, $k); } return $REWARDARR; } #处理所有超过最大值的红包 Public Function diff ($diff, & $rewardArr, $max, $min, $transfer, $k) {#将多余的钱均摊给小于最大值的红 Package for ($i = $K $i < $this->rewardnum; $i + +) {#造随机值 if ($transfer > $min *) {$AA = rand ($min, $min * 20); if ($i% 2) {$transfer + = $AA; } else {$transfer-= $AA; }} if ($rewardArr [$i] + $transfer > $max) continue; if ($diff-$transfer < 0) {$REWARDARR [$i] + = $diff; $diff = 0; Break } $REWARDARR [$i] + = $transfer; $diff-= $transfer; } if ($diff > 0) {$i + +; $this->diff ($diff, $REWARDARR, $max, $min, $transfer, $k); }} #第一个红包小于0, reduce the Public function add (& $REWARDARR, $min) {foreach ($rewardArr as & $re) from the big red envelope { $dev = Floor ($re/$min); if ($dev > 2) {$transfer = $min * Floor ($dev/2); $re-= $transfer;$REWARDARR [$this->rewardnum-1] + = $transfer; } elseif ($dev = = 2) {$re-= $min; $REWARDARR [$this->rewardnum-1] + = $min; } else {break; }} if ($rewardArr [$this->rewardnum-1] > $min | | $rewardArr [$this->rewardnum-1] = = $min) { Return } else {$this->add ($REWARDARR, $min); } }}
Detail considerations:
The following code is used to control the specific business logic, according to the specific needs, set aside a fixed maximum value, the minimum value of the red envelope amount, etc.;
Splitreward ($total, $num, $max -0.01, $min) when calling the method of generating red envelopes in code, the maximum value that I pass in is reduced by 0.01, which guarantees that the maximum value generated in the red envelope will never exceed the maximum value we set.
<?php class createreward{/* * Generate red Envelopes * Author xx September 23, 2016 13:53:38 * @param int Total amount of $total red envelope * @param int $num red Envelope Total * @param int $max red envelope Maximum * * /Public Function random_red ($total, $num, $max, $min) { #总共要发的红包金额, leave a maximum value; $total = $total-$max; $reward = new Reward (); $result _merge = $reward->splitreward ($total, $num, $max -0.01, $min); Sort ($result _merge); $result _merge[1] = $result _merge[1] + $result _merge[0]; $result _merge[0] = $max *; foreach ($result _merge as & $v) { $v = floor ($v)/; } return $result _merge; }}
Four, pull out to walk
Base code:
Set various initial values.
<?php/** * Created by Phpstorm. * User:lufei * DATE:2017/1/4 * time:22:49 */header (' content-type:text/html;charset=utf-8 '); Ini_set (' Memory_limit ', ' 128M '); require_once (' createreward.php '); require_once (' reward.php '); $total = 50000; $num = 300000; $max =; $min = 0.01; $create _reward = new Createreward ();
Performance test:
Because Memory_limit limit, so only measured 5 times the mean, the results are around 1.6s.
for ($i =0; $i <5; $i + +) { $time _start = Microtime_float (); $reward _arr = $create _reward->random_red ($total, $num, $max, $min); $time _end = Microtime_float (); $time [] = $time _end-$time _start;} echo array_sum ($time)/5;function microtime_float () { list ($usec, $sec) = Explode ("", Microtime ()); return (float) $usec + (float) $sec);}
Operation Result:
Data check:
Detection has no negative value, there is no maximum value, the maximum number of, there is no less than the minimum value;
$reward _arr = $create _reward->random_red ($total, $num, $max, $min); sort ($reward _arr);//positive order, smallest in front $sum = 0; $min _ Count = 0; $max _count = 0;foreach ($reward _arr as $i + = $val) { if ($i <3) { echo "<br/>". ( $i + 1). " A red envelope, the amount is: ". $val." <br/> "; } if ($val = = $max) { $max _count++; } if ($val < $min) { $min _count++; } $val = $val *100; $sum + = $val;} Detect if the money is all over Echo '
Operation Result:
Normal Distribution graph:
Note that when out of the picture, the number of red envelopes do not give too much, otherwise the page renders, will collapse
$reward _arr = $create _reward->random_red ($total, $num, $max, $min); $show = Array (); Rsort ($reward _arr);// For a more intuitive display of the normal distribution effect, you need to reorder the array foreach ($reward _arr as $k + = $value) { $t = $k%2; if (! $t) $show []= $value;; else Array_unshift ($show, $value);} echo "Set maximum Value:". $max. ', minimum value: '. $min. '
Operation Result:
PS: A friend asked me whether the data generated by the mathematical method to verify that it conforms to the standard normal distribution, because my math is not good, this really did not count, just look like, when he is.
Since encountering this problem, it must be solved, so I use PHP built-in function to forget, the results of the data in a small amount of time or relatively close to normal distribution, but the amount of data when it can not be seen, I do not understand this, we are interested can find the reason yo.
PHP four functions: stats_standard_deviation (Standard deviation), stats_variance (variance), stats_kurtosis (kurtosis), Stats_skew (skewness)
Use the above function to install the stats extension @ Download address
Five, in the end
Here, even if the red envelope is finished, I do not know whether it can rise 50 yuan wages, but should be able to solve the urgent need.
Oh, yes, I left this code. Package download