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


Posted in PHP onSeptember 18, 2019

 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。

<?php
namespace Werben\Tools;
use Exception;
class NumberSlicing {
 /**
  * 精确小数点,舍弃最后一位之后的数据(非四舍五入)
  * floor with precision
  * @param $number 要精确的数
  * @param $precision 精度,比如保留到0.01,则该值为2
  * @return float|int
  */
 public static function floorWithPrecision($number, $precision) {
  $power = pow(10, $precision);
  $ret = floor($number * $power) * 1.0 / $power ;
  return $ret;
 }
 /**
  * 精确小数点,按四舍五入保留最后一位
  * round with precision
  * @param $number 要精确的数
  * @param $precision 精度,比如保留到0.01,则该值为2
  * @return float|int
  */
 public static function roundWithPrecision($number, $precision) {
  $power = pow(10, $precision);
  $ret = round($number * $power) * 1.0 / $power ;
  return $ret;
 }
 /**
  * 将数把权重放大,比如1,要按精度0.0001分配,则先将1乘以10000然后再来分配
  * random the sum weights 加上权重之后,整个要切割的数的权重总值
  * @param $weight_items 用来保留,随机分配的权重值
  * @param $count 要切割的份数
  * @param int $each_weight 加上权重之后,每一份平均的权重值
  * @param int $min_weight 加上权重之后,最小额度的值
  * @return float|int
  */
 public static function weightSlicing(&$weight_items, $count, $each_weight = 10, $min_weight = 3)
 {
  $already_count = count($weight_items);
  $cur_random_full_total = ($already_count + 1) * $each_weight;
  $already_random_real_total = 0;
  foreach ($weight_items as $value) {
   $already_random_real_total += $value;
  }
  $cur_random_rest = $cur_random_full_total - $already_random_real_total;
  if ($already_count == $count - 1) {
   $cur_random_rate = $cur_random_rest;
  } else {
   $cur_random_rate_max = $cur_random_rest + $each_weight - $min_weight * 2;
   $cur_random_rate = $min_weight + mt_rand(0, $cur_random_rate_max);
  }
  $weight_items[] = $cur_random_rate;
  return $cur_random_rate;
 }
 /**
  * slicing the number
  * @param int $number
  * @param int $size
  * @param float $precision
  * @param float $min
  * @return array
  * @throws Exception
  */
 public static function numberSlicing($number, $size, $precision = 0.01, $min = 0.01) {
  if ($number * 1.0 / $size <= $min) {
   throw new Exception('min number is bigger than the average value!');
  }
  if ($precision > 1) {
   throw new Exception('precision can\'t bigger than 1!');
  }
  if ($min < $precision) {
   throw new Exception('precision can\'t bigger than min!');
  }
  $weight_items = [];
  $items = [];
  //不加权重情况下,每一份的平均值
  $each_weight = intval($number / $size);
  if ($precision < 1) {
   //如果精度是小数
   if ($each_weight > 1) {
    //如果平均值大于1,则最小额度则直接用min就可以了
    //每一份的平均值乘以权重的值,比如精度为0.01,则每一份的平均值要乘以权重(100)
    $each_weight = intval((1 / $precision) * $number / $size);
    //最小数值也要乘以权重
    $min_weight = intval(1 / $precision) * $min;
   } else {
    //如果平均值小于1,需要将平均值也乘以权重
    $each_weight = intval(1 / $precision);
    $min_weight = $each_weight * $size * $min / $number;
   }
   $precision_num = log10(1 / $precision);
  } else {
   //如果精度是整数(1)
   $min_weight = $min;
   $precision_num = 0;
  }
  $sum_item_number = 0.0;
  $sum_weight = 0.0;
  //先将整个数,随机按最小额度分配
  for ($i = 0; $i < $size; $i++) {
   $cur_weight = self::weightSlicing($weight_items, $size, $each_weight, $min_weight);
   //将权重去除,换算回原先的比例
   $rate = ($number * $cur_weight * 1.00) / ($size * $each_weight);
   $rate = self::floorWithPrecision($rate, $precision_num);
   $sum_item_number += $rate;
   $sum_weight += $cur_weight;
   $items[] = $rate;
  }
  //由于误差,随机分配后,还会遗留一些数没有完全分配完,则将剩下的数随机分配
  if ($precision_num != 0) {
   //如果是切割成小数
   $rest = $number - $sum_item_number;
   while ($rest - 0.00 > PHP_FLOAT_MIN) {
    if ($rest / $min >= 1.0) {
     //剩余的数大于min最小额度,则将每份最小额度随机分配
     $random_index = mt_rand(0, $size - 1);
     $items[$random_index] = self::roundWithPrecision($items[$random_index] + $min, $precision_num);
     $sum_item_number = self::roundWithPrecision($sum_item_number + $min, $precision_num);
     $rest = self::roundWithPrecision($number - $sum_item_number, $precision_num);
    } else {
     //剩余的数小于min最小额度,则将这最后的未分配的数随机分配
     $random_index = mt_rand(0, $size - 1);
     $items[$random_index] = self::roundWithPrecision($items[$random_index] + $number - $sum_item_number, $precision_num);
     $sum_item_number = $number;
     $rest = $number - $sum_item_number;
    }
   }
  } else {
   //如果是切割成整数
   $rest = $number - $sum_item_number;
   while ($rest > 0) {
    if ($rest / $min >= 1) {
     $random_index = mt_rand(0, $size - 1);
     $items[$random_index] += $min;
     $sum_item_number += $min;
     $rest = $number - $sum_item_number;
    } else {
     $random_index = mt_rand(0, $size - 1);
     $items[$random_index] += $rest;
     $sum_item_number += $rest;
     $rest = $number - $sum_item_number;
    }
   }
  }
  return $items;
 }
}

测试代码:

use Werben\Tools\NumberSlicing;
 
function testIntSlicing2IntOne() {
 $precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01
 $size = 10;   //切割的份数,the size of the number to slicing
 $min = 3;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008
 $number = 100;  //要切割的数字,the number
 $items = NumberSlicing::numberSlicing($number, $size, $precision, $min);
 $sum = 0.0;
 $ret_min = $number;
 foreach ($items as $value) {
  $sum += $value;
  if ($ret_min > $value) {
   $ret_min = $value;
  }
 }
 $count = count($items);
 echo "count: $count, sum: $sum, ret_min: $ret_min\n";
 echo "items : ". json_encode($items) ."\n";
}
function testIntSlicing2IntTwo() {
 $precision = 1; //精确度 eg: 1, 0.1, 0.01, 0.01
 $size = 30;   //切割的份数,the size of the number to slicing
 $min = 18666;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008
 $number = 800000;  //要切割的数字,the number
 $items = NumberSlicing::numberSlicing($number, $size, $precision, $min);
 $sum = 0.0;
 $ret_min = $number;
 foreach ($items as $value) {
  $sum += $value;
  if ($ret_min > $value) {
   $ret_min = $value;
  }
 }
 $count = count($items);
 echo "count: $count, sum: $sum, ret_min: $ret_min\n";
 echo "items : ". json_encode($items) ."\n";
}
function testIntSlicing2FloatOne() {
 $precision = 0.01; //精确度 eg: 1, 0.1, 0.01, 0.01
 $size = 1000;   //切割的份数,the size of the number to slicing
 $min = 0.05;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008
 $number = 100;  //要切割的数字,the number
 $items = NumberSlicing::numberSlicing($number, $size, $precision, $min);
 $sum = 0.0;
 $ret_min = $number;
 foreach ($items as $key => $value) {
  $sum += $value;
  if ($ret_min > $value) {
   $ret_min = $value;
  }
 }
 $count = count($items);
 echo "count: $count, sum: $sum, ret_min: $ret_min\n";
 echo "items: ". json_encode($items) ."\n";
}
function testIntSlicing2FloatTwo() {
 $precision = 0.00001; //精确度 eg: 1, 0.1, 0.01, 0.01
 $size = 1000;   //切割的份数,the size of the number to slicing
 $min = 0.00005;  //最小额度,最小额度必须大于最小精度,min amount eg: 3, 0.23, 0.05, 0.008
 $number = 5;  //要切割的数字,the number
 $items = NumberSlicing::numberSlicing($number, $size, $precision, $min);
 $sum = 0.0;
 $ret_min = $number;
 foreach ($items as $key => $value) {
  $sum += $value;
  if ($ret_min > $value) {
   $ret_min = $value;
  }
 }
 $count = count($items);
 echo "count: $count, sum: $sum, ret_min: $ret_min\n";
 echo "items: ". json_encode($items) ."\n";
}

总结

以上所述是小编给大家介绍的PHP切割整数工具类似微信红包金额分配的思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

PHP 相关文章推荐
PHPExcel读取Excel文件的实现代码
Dec 06 PHP
解析posix与perl标准的正则表达式区别
Jun 17 PHP
php有道翻译api调用方法实例
Dec 22 PHP
PHP通过API获取手机号码归属地
May 28 PHP
php如何获取文件的扩展名
Oct 28 PHP
搭建Vim为自定义的PHP开发工具的一些技巧
Dec 11 PHP
详解PHP实现执行定时任务
Dec 21 PHP
简单谈谈PHP中的include、include_once、require以及require_once语句
Apr 23 PHP
ThinkPHP发送邮件示例代码
Oct 08 PHP
使用PHP+MySql实现微信投票功能实例代码
Sep 29 PHP
Yii Framework框架开发微信公众平台示例
Apr 26 PHP
基于PHP+Mysql简单实现了图书购物车系统的实例详解
Aug 06 PHP
php实现多站点共用session实现单点登录的方法详解
Sep 18 #PHP
PHP实现批量修改文件名的方法示例
Sep 18 #PHP
php DES加密算法实例分析
Sep 18 #PHP
php实现QQ小程序发送模板消息功能
Sep 18 #PHP
php文件后缀不强制为.php的实操方法
Sep 18 #PHP
php校验公钥是否可用的实例方法
Sep 17 #PHP
php写入mysql中文乱码的实例解决方法
Sep 17 #PHP
You might like
php下把数组保存为文件格式的实例应用
2010/02/08 PHP
ajax实现无刷新分页(php)
2010/07/18 PHP
ThinkPHP3.1新特性之对Ajax的支持更加完善
2014/06/19 PHP
php图片添加文字水印实现代码
2016/03/15 PHP
Yii针对添加行的增删改查操作示例
2016/10/18 PHP
关于Curl在Swoole协程中的解决方案详析
2019/09/12 PHP
Javascript的一种模块模式
2010/09/08 Javascript
document.documentElement的一些使用技巧
2013/04/18 Javascript
jquery对象和DOM对象的区别介绍
2013/08/09 Javascript
jQuery实现点击小图片淡入淡出显示大图片特效
2015/09/09 Javascript
JS实现从顶部下拉显示的带动画QQ客服特效代码
2015/10/24 Javascript
jQuery+canvas实现的球体平抛及颜色动态变换效果
2016/01/28 Javascript
jQuery实现元素拖拽并cookie保存顺序的方法
2016/02/20 Javascript
Iscrool下拉刷新功能实现方法(推荐)
2017/06/26 Javascript
详解Angular Reactive Form 表单验证
2017/07/06 Javascript
jQuery+ajax实现动态添加表格tr td功能示例
2018/04/23 jQuery
微信小程序中换行空格(多个空格)写法详解
2018/07/10 Javascript
js中null与空字符串&quot;&quot;的区别讲解
2019/01/17 Javascript
koa2 用户注册、登录校验与加盐加密的实现方法
2019/07/22 Javascript
Element-UI+Vue模式使用总结
2020/01/02 Javascript
跟老齐学Python之从格式化表达式到方法
2014/09/28 Python
详解Python 2.6 升级至 Python 2.7 的实践心得
2017/04/27 Python
Python实现爬取需要登录的网站完整示例
2017/08/19 Python
对pandas中apply函数的用法详解
2018/04/10 Python
Python实现读取字符串按列分配后按行输出示例
2018/04/17 Python
使用python对文件中的数值进行累加的实例
2018/11/28 Python
Django 多环境配置详解
2019/05/14 Python
18个Python脚本可加速你的编码速度(提示和技巧)
2019/10/17 Python
Python进程Multiprocessing模块原理解析
2020/02/28 Python
纯CSS和jQuery实现的在页面顶部显示的进度条效果2例(仿手机浏览器进度条效果)
2014/04/16 HTML / CSS
HTML5新特性 多线程(Worker SharedWorker)
2017/04/24 HTML / CSS
波兰家具和室内装饰品购物网站:Vivre
2018/04/10 全球购物
编程输出如下图形
2013/11/24 面试题
计算机专业毕业生自荐书
2014/06/02 职场文书
2015教师个人德育工作总结
2015/07/22 职场文书
实验室安全管理制度
2015/08/05 职场文书