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 相关文章推荐
一个PHP+MSSQL分页的例子
Oct 09 PHP
memcached 和 mysql 主从环境下php开发代码详解
May 16 PHP
PHP daddslashes 使用方法介绍
Oct 26 PHP
php中将数组转成字符串并保存到数据库中的函数代码
Sep 29 PHP
php实现在线生成条形码示例分享(条形码生成器)
Dec 30 PHP
php的memcache类分享(memcache队列)
Mar 26 PHP
推荐几个开源的微信开发项目
Dec 28 PHP
php实现window平台的checkdnsrr函数
May 27 PHP
部署PHP时的4个配置修改说明
Oct 19 PHP
PHP模板引擎Smarty内建函数section,sectionelse用法详解
Apr 11 PHP
浅谈使用 Yii2 AssetBundle 中 $publishOptions 的正确姿势
Nov 08 PHP
Laravel框架实现调用百度翻译API功能示例
May 30 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
DOTA2 玩家自创拉野攻略 特色英雄快速成长篇
2020/04/20 DOTA
在PWS上安装PHP4.0正式版
2006/10/09 PHP
PHP通过session id 实现session共享和登录验证的代码
2012/06/03 PHP
PHP错误WARNING: SESSION_START() [FUNCTION.SESSION-START]解决方法
2014/05/04 PHP
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
2010/03/16 Javascript
js判读浏览器是否支持html5的canvas的代码
2013/11/18 Javascript
jQuery向上遍历DOM树之parents(),parent(),closest()之间的区别
2013/12/02 Javascript
jquery上传插件fineuploader上传文件使用方法(jquery图片上传插件)
2013/12/05 Javascript
利用JS来控制键盘的上下左右键(示例代码)
2013/12/14 Javascript
文本域光标操作的jQuery扩展分享
2014/03/10 Javascript
js实现按钮加背景图片常用方法
2014/11/01 Javascript
文本框只能输入数字的实现方法(兼容IE火狐)
2016/06/25 Javascript
基于Bootstrap的网页设计实例
2017/03/01 Javascript
es7学习教程之Decorators(修饰器)详解
2017/07/21 Javascript
Angular2的管道Pipe的使用方法
2017/11/07 Javascript
vue 使用Jade模板写html,stylus写css的方法
2018/02/23 Javascript
JS中双击和单击事件冲突的解决方法
2018/04/09 Javascript
详解通过源码解析Node.js中cluster模块的主要功能实现
2018/05/16 Javascript
node.js中express模块创建服务器和http模块客户端发请求
2019/03/06 Javascript
js微信分享接口调用详解
2019/07/23 Javascript
vue中动态select的使用方法示例
2019/10/28 Javascript
微信小程序实用代码段(收藏版)
2019/12/17 Javascript
JS变量提升及函数提升实例解析
2020/09/03 Javascript
python 获取字符串MD5值方法
2018/05/29 Python
Keras 在fit_generator训练方式中加入图像random_crop操作
2020/07/03 Python
一些关于python 装饰器的个人理解
2020/08/31 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
6种非常炫酷的CSS3按钮边框动画特效
2016/03/16 HTML / CSS
VSCode 自定义html5模板的实现
2019/12/05 HTML / CSS
俄罗斯电子产品、计算机和家用电器购物网站:OLDI
2019/10/27 全球购物
医学生实习自我鉴定
2013/09/27 职场文书
幼儿园五一活动方案
2014/02/07 职场文书
广播体操口号
2014/06/18 职场文书
2014财务人员自我评价范文
2014/09/21 职场文书
2014年班组长工作总结
2014/11/20 职场文书
2016三八妇女节慰问信
2015/11/30 职场文书