ArithmeticHelper.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. namespace common\helpers;
  3. /**
  4. * 算法辅助类
  5. *
  6. * Class ArithmeticHelper
  7. * @package common\helpers
  8. * @author jianyan74 <751393839@qq.com>
  9. */
  10. class ArithmeticHelper
  11. {
  12. /**
  13. * 生成红包算法(随机金额)
  14. *
  15. * @param number $money 红包总金额
  16. * @param number $num 生成的红包数量
  17. * @param number $min 红包最小金额
  18. * @param number $max 红包最大金额
  19. * @return array
  20. */
  21. public static function getRedPackage($money, $num, $min, $max)
  22. {
  23. $data = [];
  24. // 判断最小红包乘数量是否大于总金额
  25. if ($min * $num > $money) {
  26. return $data;
  27. }
  28. // 判断最大红包乘数量是否小于总金额
  29. if ($max * $num < $money) {
  30. return $data;
  31. }
  32. while ($num >= 1) {
  33. $num--;
  34. $kmix = max($min, $money - $num * $max);
  35. $kmax = min($max, $money - $num * $min);
  36. $kAvg = $money / ($num + 1);
  37. // 获取最大值和最小值的距离之间的最小值
  38. $kDis = min($kAvg - $kmix, $kmax - $kAvg);
  39. // 获取0到1之间的随机数与距离最小值相乘得出浮动区间,这使得浮动区间不会超出范围
  40. $r = ((float)(rand(1, 10000) / 10000) - 0.5) * $kDis * 2;
  41. $k = round($kAvg + $r, 2);
  42. $money -= $k;
  43. $data[] = $k;
  44. }
  45. shuffle($data);
  46. return $data;
  47. }
  48. /**
  49. * 生成红包算法(固定金额)
  50. *
  51. * @param number $money 红包总金额
  52. * @param number $num 生成的红包数量
  53. * @param number $min 红包最小金额
  54. * @param number $max 红包最大金额
  55. * @return array
  56. */
  57. public static function getFixationRedPackage($money, $num)
  58. {
  59. $data = [];
  60. // 判断最小红包乘数量是否大于总金额
  61. if (0.01 * $num > $money) {
  62. return $data;
  63. }
  64. // 单个红包金额
  65. $single = BcHelper::div($money, $num);
  66. $remainder = 0;
  67. if (($total = BcHelper::mul($single, $num)) < $money) {
  68. $remainder = BcHelper::sub($money, $total);
  69. }
  70. for ($i = 0; $i < $num; $i++) {
  71. $data[] = $single;
  72. }
  73. $data[0] = BcHelper::add($data[0], $remainder);
  74. shuffle($data);
  75. return $data;
  76. }
  77. /** ------ 抽奖算法(摇一摇,拉霸机,刮刮乐) ------ **/
  78. /**
  79. * 非必中 总概率1-1000
  80. * @param array $awards 奖品数组
  81. * @param string $prob 奖品概率
  82. * @param string $key 返回的数组键值
  83. * @return bool
  84. */
  85. public static function drawRandom($awards = [], $prob = 'prob', $key = 'id')
  86. {
  87. $rand = mt_rand(1, 1000);
  88. $proArr = [];
  89. $pro = 0;
  90. // 按概率抽奖
  91. foreach ($awards as $award) {
  92. $pro += $award[$prob];
  93. $proArr[] = $pro;
  94. }
  95. foreach ($proArr as $k => $v) {
  96. if ($rand < $v) {
  97. return $awards[$k][$key];
  98. break;
  99. }
  100. }
  101. return false;
  102. }
  103. /**
  104. * 抽奖必中
  105. *
  106. * @param array $awards 奖品数组
  107. * @param string $prob 奖品概率
  108. * @return bool
  109. */
  110. public static function drawBitslap($awards = [], $prob = 'prob')
  111. {
  112. $proArr = [];
  113. if ($awards) {
  114. foreach ($awards as $key => $value) {
  115. $proArr[$key] = $value[$prob];
  116. }
  117. $result = self::getDrawRand($proArr);
  118. return $awards[$result]['id'];
  119. }
  120. return false;
  121. }
  122. /**
  123. * 经典的概率算法
  124. *
  125. * $proArr是一个预先设置的数组,
  126. * 假设数组为:array(100,200,300,400),
  127. * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内,
  128. * 如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间,
  129. * 在本例当中就是减去100,也就是说第二个数是在1,900这个范围内筛选的。
  130. * 这样 筛选到最终,总会有一个数满足要求。
  131. * 就相当于去一个箱子里摸东西,
  132. * 第一个不是,第二个不是,第三个还不是,那最后一个一定是。
  133. * 这个算法简单,而且效率非常高,
  134. * 关键是这个算法已在我们以前的项目中有应用,尤其是大数据量的项目中效率非常棒。
  135. */
  136. public static function getDrawRand($proArr = [])
  137. {
  138. $result = '';
  139. // 概率数组的总概率精度
  140. $proSum = array_sum($proArr);
  141. // 概率数组循环
  142. foreach ($proArr as $key => $proCur) {
  143. $randNum = mt_rand(1, $proSum);
  144. if ($randNum <= $proCur) {
  145. $result = $key;
  146. break;
  147. } else {
  148. $proSum -= $proCur;
  149. }
  150. }
  151. unset ($proArr);
  152. return $result;
  153. }
  154. }
粤ICP备19079148号