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。

<?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切割整数工具类似微信红包金额分配的思路详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(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. <?p

  • python编写微信公众号首图思路详解

    前言 之前一直在美图秀秀调整自己的微信公众号首图,效果也不尽如人意,老是调来调去,最后发出来的图片被裁剪了一大部分,丢失部分关键信息,十分恼火,于是想着用python写一个程序,把微信公众号首图的模式固定下来,方便以后写公众号. 思路 根据微信公众号首图要求,可以上传一个不超过5M的图片,且图片尺寸要是2.35:1的尺寸,换算成像素是900:383,有了这些参数就可以做文章了,这里有两种思路 把今天推文的标题(文字)用图片展示出来,使得文字排列错落有致,简单粗暴,而又不失美感,这里可以利用mat

  • PHP实现微信红包金额拆分试玩的算法示例

    本文介绍了PHP实现微信红包金额拆分试玩的算法示例,分享给大家,有兴趣的可以看一下: <?php // 新年红包金额拆分试玩 class CBonus { public $bonus;//红包 public $bonus_num;//红包个数 public $bonus_money;//红包总金额 public $money_single_max;//单个红包限额 public function __construct(){ $this->bonus_num = 10; $this->b

  • ThinkPHP实现微信支付(jsapi支付)流程教程详解

    之前写过一篇文章讲了PHP实现微信支付(jsapi支付)流程 ,详见文章:PHP实现微信支付(jsapi支付)流程. 当时的环境是没有使用框架的,直接在一个域名指向的目录下边新建目录之后访问该目录实现的,但应用到框架中,还是有一些问题,在ThinkPHP中,由于路由规则与支付授权目录有出入,所以会报错.本篇讲讲在TP中集成微信支付的流程. 鹅厂出的SDK和文档,就是让你看不懂,使劲绕,这酸爽用了就知道.文档和SDK不是应该越简单通俗易懂越好么?难道只有使劲重构才能显示出鹅厂程序猿技术的高超咩?额

  • PHP实现微信支付(jsapi支付)流程步骤详解

    最近接触到一个项目,涉及到微信支付,搞微信开发这么久以来,还没搞过支付,之前也就搞过公众号发红包,感谢前辈们的探索,我看了他们的博文,让我少走了很多弯路. 前期准备: 1.微信认证服务号,并且开通了微信支付 2.微信支付SDK,下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 3.登录微信支付平台https://pay.weixin.qq.com/index.php/account/api_cert下载支付证书

  • JS技巧动手实现红包兔子雨效果示例详解

    目录 前言 展示效果 技术栈 思考与实现 红包下落效果 生成红包雨 打开红包效果 兔子雨效果 拓展 设置中奖概率 后记 前言 人生天地之间,若白驹过隙,忽然而已.不知不觉中,2022年已然逝去,2023年也过去了半个月了.看到「兔了个兔」这个活动几天了,不过这周比较忙,没时间参与. 心血来潮,捣鼓了一晚上,实现一个兔年的红包雨(兔子雨)效果~ 展示效果 技术栈 Vue2 SCSS:实现红包雨(兔子雨)效果.按钮交互效果等. 思考与实现 首先,思考一下最终的展示效果:在屏幕上会有很多随机下落的兔子

  • iOS开发微信收款到账语音提醒功能思路详解

    一.背景 为了解决小商户老板们在频繁交易中不方便核对.确认到账的痛点,产品MM提出了新版本需要支持收款到账语音提醒功能.这篇文章总结了开发过程中遇到的坑和一些小技巧. 二.技术方案 后台唤醒App 收款到账语音提醒需要收款方在收到款后,播放一段TTS合成语音播报金额,微信在前台时可以通过模板消息将需要播报的金额带下来,再请求TTS数据并播放,但是app在挂起或者被kill掉的情况下要如何请求语音数据并播放呢? iOS提供了两种方式唤醒处于挂起或已经被kill掉的app.分别是Silent Not

  • 微信小程序 Buffer缓冲区的详解

     微信小程序 Buffer缓冲区的详解 JavaScript 语言自身只有字符串数据类型,没有二进制数据类型. 但在处理像TCP流或文件流时,必须使用到二进制数据.因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区. 在 node.js 中,Buffer 类是随 Node 内核一起发布的核心库.Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动

  • 微信小程序实现分页查询详解

    目录 创建自定义连接器 云开发介绍 分页实现思路 使用连接器 为什么要自定义分页功能 日常小程序经常需要分页查询的功能,本篇我们讲解一下低代码中如何实现分页查询的功能.要自己开发分页功能,可以先参考官方的方法 分页查询我们一般是需要有入参和出参,入参分别需要页码.每页大小.排序字段名称.排序方式.查询条件. 出参分别需要记录总条数.页码.每页大小.记录列表. 入参和出参知道之后,那在哪写代码呢?像分页这种功能一般属于后端的能力,低码工具中是在自定义连接器里写后端代码的. 创建自定义连接器 登录低

  • 微信小程序中input标签详解及简单实例

    微信小程序中input标签详解及简单实例 使用input标签,我们都会,在微信小程序中使用,必定也是可以一下子就会的,但是却有些常用的属性无法按照习惯去使用: 我就用我最常用的来做例子: 一个一个来解读: 首先,我是定义了他的id,这是我们最常用的,所以就配了一个id,毕竟不操作他,又为什么设成输入框呢, 第二,设置他的样式, 第三,设置他的输入类别,以上都是很简单的 第四.使用正则l:哎限定输入为纯数字.这点可能有点不理解,这是对他的keyup事件监听,将不是纯数字的list无视掉.注意,是对

随机推荐