当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP切割整数工具类似微信红包金额分配的思路详解

发布:smiling 来源: PHP粉丝网  添加日期:2021-12-19 11:28:36 浏览: 评论:0 

这篇文章主要介绍了 PHP切割整数工具类似微信红包金额分配的思路详解,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下。

Composer地址:https://packagist.org/packages/werbenhu/php-number-slicing

GitHub地址:https://github.com/werbenhu/php-number-slicing

主要代码:NumberSlicing.php

思路:将数字按精度放大倍数,比如切割数字1,切割的份数是10,精度是0.01,则将1放大100 X 10倍,然后再来对加了1000倍权重后的值进行切割,切割完成之后,再将权重去除,保证总值是1。

  1. <?php 
  2. namespace Werben\Tools; 
  3. use Exception; 
  4. class NumberSlicing { 
  5.  /** 
  6.   * 精确小数点,舍弃最后一位之后的数据(非四舍五入) 
  7.   * floor with precision 
  8.   * @param $number 要精确的数 
  9.   * @param $precision 精度,比如保留到0.01,则该值为2 
  10.   * @return float|int 
  11.   */ 
  12.  public static function floorWithPrecision($number$precision) { 
  13.   $power = pow(10, $precision); 
  14.   $ret = floor($number * $power) * 1.0 / $power ; 
  15.   return $ret
  16.  } 
  17.  /** 
  18.   * 精确小数点,按四舍五入保留最后一位 
  19.   * round with precision 
  20.   * @param $number 要精确的数 
  21.   * @param $precision 精度,比如保留到0.01,则该值为2 
  22.   * @return float|int 
  23.   */ 
  24.  public static function roundWithPrecision($number$precision) { 
  25.   $power = pow(10, $precision); 
  26.   $ret = round($number * $power) * 1.0 / $power ; 
  27.   return $ret
  28.  } 
  29.  /** 
  30.   * 将数把权重放大,比如1,要按精度0.0001分配,则先将1乘以10000然后再来分配 
  31.   * random the sum weights 加上权重之后,整个要切割的数的权重总值 
  32.   * @param $weight_items 用来保留,随机分配的权重值 
  33.   * @param $count 要切割的份数 
  34.   * @param int $each_weight 加上权重之后,每一份平均的权重值 
  35.   * @param int $min_weight 加上权重之后,最小额度的值 
  36.   * @return float|int 
  37.   */ 
  38.  public static function weightSlicing(&$weight_items$count$each_weight = 10, $min_weight = 3) 
  39.  { 
  40.   $already_count = count($weight_items); 
  41.   $cur_random_full_total = ($already_count + 1) * $each_weight
  42.   $already_random_real_total = 0; 
  43.   foreach ($weight_items as $value) { 
  44.    $already_random_real_total += $value
  45.   } 
  46.   $cur_random_rest = $cur_random_full_total - $already_random_real_total
  47.   if ($already_count == $count - 1) { 
  48.    $cur_random_rate = $cur_random_rest
  49.   } else { 
  50.    $cur_random_rate_max = $cur_random_rest + $each_weight - $min_weight * 2; 
  51.    $cur_random_rate = $min_weight + mt_rand(0, $cur_random_rate_max); 
  52.   } 
  53.   $weight_items[] = $cur_random_rate
  54.   return $cur_random_rate
  55.  } 
  56.  /** 
  57.   * slicing the number 
  58.   * @param int $number 
  59.   * @param int $size 
  60.   * @param float $precision 
  61.   * @param float $min 
  62.   * @return array 
  63.   * @throws Exception 
  64.   */ 
  65.  public static function numberSlicing($number$size$precision = 0.01, $min = 0.01) { 
  66.   if ($number * 1.0 / $size <= $min) { 
  67.    throw new Exception('min number is bigger than the average value!'); 
  68.   } 
  69.   if ($precision > 1) { 
  70.    throw new Exception('precision can\'t bigger than 1!'); 
  71.   } 
  72.   if ($min < $precision) { 
  73.    throw new Exception('precision can\'t bigger than min!'); 
  74.   } 
  75.   $weight_items = []; 
  76.   $items = []; 
  77.   //不加权重情况下,每一份的平均值 
  78.   $each_weight = intval($number / $size); 
  79.   if ($precision < 1) { 
  80.    //如果精度是小数 
  81.    if ($each_weight > 1) { 
  82.     //如果平均值大于1,则最小额度则直接用min就可以了 
  83.     //每一份的平均值乘以权重的值,比如精度为0.01,则每一份的平均值要乘以权重(100) 
  84.     $each_weight = intval((1 / $precision) * $number / $size); 
  85.     //最小数值也要乘以权重 
  86.     $min_weight = intval(1 / $precision) * $min
  87.    } else { 
  88.     //如果平均值小于1,需要将平均值也乘以权重 
  89.     $each_weight = intval(1 / $precision); 
  90.     $min_weight = $each_weight * $size * $min / $number
  91.    } 
  92.    $precision_num = log10(1 / $precision); 
  93.   } else { 
  94.    //如果精度是整数(1) 
  95.    $min_weight = $min
  96.    $precision_num = 0; 
  97.   } 
  98.   $sum_item_number = 0.0; 
  99.   $sum_weight = 0.0; 
  100.   //先将整个数,随机按最小额度分配 
  101.   for ($i = 0; $i < $size$i++) { 
  102.    $cur_weight = self::weightSlicing($weight_items$size$each_weight$min_weight); 
  103.    //将权重去除,换算回原先的比例 
  104.    $rate = ($number * $cur_weight * 1.00) / ($size * $each_weight); 
  105.    $rate = self::floorWithPrecision($rate$precision_num); 
  106.    $sum_item_number += $rate
  107.    $sum_weight += $cur_weight
  108.    $items[] = $rate
  109.   } 
  110.   //由于误差,随机分配后,还会遗留一些数没有完全分配完,则将剩下的数随机分配 
  111.   if ($precision_num != 0) { 
  112.    //如果是切割成小数 
  113.    $rest = $number - $sum_item_number
  114.    while ($rest - 0.00 > PHP_FLOAT_MIN) { 
  115.     if ($rest / $min >= 1.0) { 
  116.      //剩余的数大于min最小额度,则将每份最小额度随机分配 
  117.      $random_index = mt_rand(0, $size - 1); 
  118.      $items[$random_index] = self::roundWithPrecision($items[$random_index] + $min$precision_num); 
  119.      $sum_item_number = self::roundWithPrecision($sum_item_number + $min$precision_num); 
  120.      $rest = self::roundWithPrecision($number - $sum_item_number$precision_num); 
  121.     } else { 
  122.      //剩余的数小于min最小额度,则将这最后的未分配的数随机分配 
  123.      $random_index = mt_rand(0, $size - 1); 
  124.      $items[$random_index] = self::roundWithPrecision($items[$random_index] + $number - $sum_item_number$precision_num); 
  125.      $sum_item_number = $number
  126.      $rest = $number - $sum_item_number
  127.     } 
  128.    } 
  129.   } else { 
  130.    //如果是切割成整数 
  131.    $rest = $number - $sum_item_number
  132.    while ($rest > 0) { 
  133.     if ($rest / $min >= 1) { 
  134.      $random_index = mt_rand(0, $size - 1); 
  135.      $items[$random_index] += $min
  136.      $sum_item_number += $min
  137.      $rest = $number - $sum_item_number
  138.     } else { 
  139.      $random_index = mt_rand(0, $size - 1); 
  140.      $items[$random_index] += $rest
  141.      $sum_item_number += $rest
  142.      $rest = $number - $sum_item_number
  143.     } 
  144.    } 
  145.   } 
  146.   return $items
  147.  } 

测试代码:

  1. use Werben\Tools\NumberSlicing; 
  2.    
  3. function testIntSlicing2IntOne() { 
  4.  $precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01 
  5.  $size = 10;   //切割的份数,the size of the number to slicing 
  6.  $min = 3;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008 
  7.  $number = 100;  //要切割的数字,the number 
  8.  $items = NumberSlicing::numberSlicing($number$size$precision$min); 
  9.  $sum = 0.0; 
  10.  $ret_min = $number
  11.  foreach ($items as $value) { 
  12.   $sum += $value
  13.   if ($ret_min > $value) { 
  14.    $ret_min = $value
  15.   } 
  16.  } 
  17.  $count = count($items); 
  18.  echo "count: $count, sum: $sum, ret_min: $ret_min\n"
  19.  echo "items : ". json_encode($items) ."\n"
  20. function testIntSlicing2IntTwo() { 
  21.  $precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01 
  22.  $size = 30;   //切割的份数,the size of the number to slicing 
  23.  $min = 18666;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008 
  24.  $number = 800000;  //要切割的数字,the number 
  25.  $items = NumberSlicing::numberSlicing($number$size$precision$min); 
  26.  $sum = 0.0; 
  27.  $ret_min = $number
  28.  foreach ($items as $value) { 
  29.   $sum += $value
  30.   if ($ret_min > $value) { 
  31.    $ret_min = $value
  32.   } 
  33.  } 
  34.  $count = count($items); 
  35.  echo "count: $count, sum: $sum, ret_min: $ret_min\n"
  36.  echo "items : ". json_encode($items) ."\n"
  37. function testIntSlicing2FloatOne() { 
  38.  $precision = 0.01; //精确度 eg: 1, 0.1, 0.01, 0.01 
  39.  $size = 1000;   //切割的份数,the size of the number to slicing 
  40.  $min = 0.05;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008 
  41.  $number = 100;  //要切割的数字,the number 
  42.  $items = NumberSlicing::numberSlicing($number$size$precision$min); 
  43.  $sum = 0.0; 
  44.  $ret_min = $number
  45.  foreach ($items as $key => $value) { 
  46.   $sum += $value
  47.   if ($ret_min > $value) { 
  48.    $ret_min = $value
  49.   } 
  50.  } 
  51.  $count = count($items); 
  52.  echo "count: $count, sum: $sum, ret_min: $ret_min\n"
  53.  echo "items: ". json_encode($items) ."\n"
  54. function testIntSlicing2FloatTwo() { 
  55.  $precision = 0.00001; //精确度 eg: 1, 0.1, 0.01, 0.01 
  56.  $size = 1000;   //切割的份数,the size of the number to slicing 
  57.  $min = 0.00005;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008 
  58.  $number = 5;  //要切割的数字,the number 
  59.  $items = NumberSlicing::numberSlicing($number$size$precision$min); 
  60.  $sum = 0.0; 
  61.  $ret_min = $number
  62.  foreach ($items as $key => $value) { 
  63.   $sum += $value
  64.   if ($ret_min > $value) { 
  65.    $ret_min = $value
  66.   } 
  67.  } 
  68.  $count = count($items); 
  69.  echo "count: $count, sum: $sum, ret_min: $ret_min\n"
  70.  echo "items: ". json_encode($items) ."\n"
  71. }

Tags: PHP切割整数 PHP微信红包金额

分享到: