1 需求

写一个固定红包 + 随机红包

固定红包就是每个红包金额一样,有多少个就发多少个固定红包金额就行。

随机红包的需求是。比如红包总金额5元,需要发10个红包。随机范围是 0.01到0.99;5元必需发完,金额需要有一定趋势的正态分布。(0.99可以任意指定,也可以是 avg * 2 - 0.01;比如avg = 5 / 10 = 0.5;(avg * 2 - 0.01 = 0.99))

2 需求分析

2.1 固定红包

如果是固定红包,则算法是一条直线。t就是固定红包的额度。如图。

f(x) = t;(1 <= x <= num)

image.png

2.2 随机红包

如果我们使用随机函数rand。rand(0.01,0.99);那么10次随机,如果最坏情况都是金额0.99,总金额就是9.9元。会超过5元。金额也会不正态分布。最后思考了一下借助与数学函数来当作随机红包的发生器,可以用抛物线,三角函数。最后选定了等腰三角线性函数。

1 算法原理

如果需要发红包总金额是totalMoney,红包个数是num个,金额范围是[min,max],线性方程如图。

image.png

三个点的坐标:

(x1,y1) = (1,min)

(x2,y2) = (num/2,max)

(x3,y3) = (num,min)

确定的线性方程:

$y = 1.0 * ($x - $x1) / ($x2 - $x1) * ($y2 - $y1) + $y1 ; (x1 <= x <= x2)

$y = 1.0 * ($x - $x2) / ($x3 - $x2) * ($y3 - $y2) + $y2; (x2 <= x <= x3)

修数据:

y(合) = y1 + y2 + y3 +...... ynum;

y(合)有可能 > totalMoney ,说明生成金额多了,需要修数据,则从(y1,y2,y3.....ynum)这些每次减少0.01。直到y(合) = totalMoney。

y(合)有可能 < totalMoney ,说明生成金额少了,需要修数据,则从(y1,y2,y3.....ynum)这些每次加上0.01。直到y(合) = totalMoney。

2 算法原理样例

如果需要发红包总金额是11470,红包个数是7400个,金额范围是[0.01,3.09],线性方程如图。

image.png

3 需求设计

3.1 类图设计

image.png

3.2 源码设计

/**

* 随机红包+固定红包算法[策略模式]

* copyright (c) 2016 http://blog.csdn.net/CleverCode

*/

//配置传输数据DTO

class OptionDTO

{

//红包总金额

public $totalMoney;

//红包数量

public $num;

//范围开始

public $rangeStart;

//范围结算

public $rangeEnd;

//生成红包策略

public $builderStrategy;

//随机红包剩余规则

public $randFormatType; //Can_Left:不修数据,可以有剩余;No_Left:不能有剩余

public static function create($totalMoney,$num,$rangeStart,$rangEnd,

$builderStrategy,$randFormatType = 'No_Left')

{

$self = new self();

$self->num = $num;

$self->rangeStart = $rangeStart;

$self->rangeEnd = $rangEnd;

$self->totalMoney = $totalMoney;

$self->builderStrategy = $builderStrategy;

$self->randFormatType = $randFormatType;

return $self;

}

}

//红包生成器接口

interface IBuilderStrategy

{

//创建红包

public function create();

//设置配置

public function setOption(OptionDTO $option);

//是否可以生成红包

public function isCanBuilder();

//生成红包函数

public function fx($x);

}

//固定等额红包策略

class EqualPackageStrategy implements IBuilderStrategy

{

//单个红包金额

public $oneMoney;

//数量

public $num;

public function __construct($option = null)

{

if($option instanceof OptionDTO)

{

$this->setOption($option);

}

}

public function setOption(OptionDTO $option)

{

$this->oneMoney = $option->rangeStart;

$this->num = $option->num;

}

public function create()

{

$data = array();

if(false == $this->isCanBuilder())

{

return $data;

}

$data = array();

if(false == is_int($this->num) || $this->num <= 0)

{

return $data;

}

for($i = 1;$i <= $this->num;$i++)

{

$data[$i] = $this->fx($i);

}

return $data;

}

/**

* 等额红包的方程是一条直线

*

* @param mixed $x

* @access public

* @return void

*/

public function fx($x)

{

return $this->oneMoney;

}

/**

* 是否能固定红包

*

* @access public

* @return void

*/

public function isCanBuilder()

{

if(false == is_int($this->num) || $this->num <= 0)

{

return false;

}

if(false == is_numeric($this->oneMoney) || $this->oneMoney <= 0)

{

return false;

}

//单个红包小于1分

if($this->oneMoney < 0.01)

{

return false;

}

return true;

}

}

//随机红包策略(三角形)

class RandTrianglePackageStrategy implements IBuilderStrategy

{

//总额

public $totalMoney;

//红包数量

public $num;

//随机红包最小值

public $minMoney;

//随机红包最大值

public $maxMoney;

//修数据方式:NO_LEFT: 红包总额 = 预算总额;CAN_LEFT: 红包总额 <= 预算总额

public $formatType;

//预算剩余金额

public $leftMoney;

public function __construct($option = null)

{

if($option instanceof OptionDTO)

{

$this->setOption($option);

}

}

public function setOption(OptionDTO $option)

{

$this->totalMoney = $option->totalMoney;

$this->num = $option->num;

$this->formatType = $option->randFormatType;

$this->minMoney = $option->rangeStart;

$this->maxMoney = $option->rangeEnd;

$this->leftMoney = $this->totalMoney;

}

/**

* 创建随机红包

*

* @access public

* @return void

*/

public function create()

{

$data = array();

if(false == $this->isCanBuilder())

{

return $data;

}

$leftMoney = $this->leftMoney;

for($i = 1;$i <= $this->num;$i++)

{

$data[$i] = $this->fx($i);

$leftMoney = $leftMoney - $data[$i];

}

//修数据

list($okLeftMoney,$okData) = $this->format($leftMoney,$data);

//随机排序

shuffle($okData);

$this->leftMoney = $okLeftMoney;

return $okData;

}

/**

* 是否能够发随机红包

* @access public

* @return void

*/

public function isCanBuilder()

{

if(false == is_int($this->num) || $this->num <= 0)

{

return false;

}

if(false == is_numeric($this->totalMoney) || $this->totalMoney <= 0)

{

return false;

}

//均值

$avgMoney = $this->totalMoney / 1.0 / $this->num;

//均值小于最小值

if($avgMoney < $this->minMoney )

{

return false;

}

return true;

}

/**

* 获取剩余金额

* @access public

* @return void

*/

public function getLeftMoney()

{

return $this->leftMoney;

}

/**

* 随机红包生成函数。三角函数。[(1,0.01),($num/2,$avgMoney),($num,0.01)]

* @param mixed $x,1 <= $x <= $this->num;

* @access public

* @return void

*/

public function fx($x)

{

if(false == $this->isCanBuilder())

{

return 0;

}

if($x < 1 || $x > $this->num)

{

return 0;

}

$x1 = 1;

$y1 = $this->minMoney;

//中间点

$x2 = ceil($this->num / 1.0 / 2);

//我的峰值

$y2 = $this->maxMoney;

//最后点

$x3 = $this->num;

$y3 = $this->minMoney;

//当x1,x2,x3都是1的时候(竖线)

if($x1 == $x2 && $x2 == $x3)

{

return $y2;

}

// '/_\'三角形状的线性方程

//'/'部分

if($x1 != $x2 && $x >= $x1 && $x <= $x2)

{

、 $y = 1.0 * ($x - $x1) / ($x2 - $x1) * ($y2 - $y1) + $y1;

return number_format($y, 2, '.', '');

}

//'\'形状

if($x2 != $x3 && $x >= $x2 && $x <= $x3)

{

$y = 1.0 * ($x - $x2) / ($x3 - $x2) * ($y3 - $y2) + $y2;

return number_format($y, 2, '.', '');

}

return 0;

}

/**

* 格式化修红包数据

*

* @param mixed $leftMoney

* @param array $data

* @access public

* @return void

*/

private function format($leftMoney,array $data)

{

//不能发随机红包

if(false == $this->isCanBuilder())

{

return array($leftMoney,$data);

}

//红包剩余是0

if(0 == $leftMoney) //无需修数据

{

return array($leftMoney,$data);

}

//数组为空

if(count($data) < 1)

{

return array($leftMoney,$data);

}

//如果是可以有剩余,并且$leftMoney > 0

if('Can_Left' == $this->formatType

&& $leftMoney > 0)

{

return array($leftMoney,$data);

}

// 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。

while($leftMoney > 0)

{

$found = 0;

foreach($data as $key => $val)

{

//减少循环优化

if($leftMoney <= 0)

{

break;

}

//预判

$afterLeftMoney = (double)$leftMoney - 0.01;

$afterVal = (double)$val + 0.01;

if( $afterLeftMoney >= 0 && $afterVal <= $this->maxMoney)

{

$found = 1;

$data[$key] = number_format($afterVal,2,'.','');

$leftMoney = $afterLeftMoney;

//精度

$leftMoney = number_format($leftMoney,2,'.','');

}

}

//如果没有可以加的红包,需要结束,否则死循环

//也就是会出现每个红包不分钱的情况,比如红包都已经最大值。这时必须在分的时候给予标志,防止死循环。

if($found == 0)

{

break;

}

}

//如果$leftMoney < 0 ,说明生成的红包超过预算了,需要减少部分红包金额

while($leftMoney < 0)

{

$found = 0;

foreach($data as $key => $val)

{

if($leftMoney >= 0)

{

break;

}

//预判

$afterLeftMoney = (double)$leftMoney + 0.01;

$afterVal = (double)$val - 0.01;

if( $afterLeftMoney <= 0 && $afterVal >= $this->minMoney)

{

$found = 1;

$data[$key] = number_format($afterVal,2,'.','');

$leftMoney = $afterLeftMoney;

$leftMoney = number_format($leftMoney,2,'.','');

}

}

//如果一个减少的红包都没有的话,需要结束,否则死循环

if($found == 0)

{

break;

}

}

return array($leftMoney,$data);

}

}

//维护策略的环境类

class RedPackageBuilder

{

// 实例

protected static $_instance = null;

/**

* Singleton instance(获取自己的实例)

*

* @return MemcacheOperate

*/

public static function getInstance()

{

if (null === self::$_instance)

{

self::$_instance = new self();

}

return self::$_instance;

}

/**

* 获取策略【使用反射】

*

* @param string $type 类型

* @return void

*/

public function getBuilderStrategy($type)

{

$class = $type.'PackageStrategy';

if(class_exists($class))

{

return new $class();

}

else

{

throw new Exception("{$class} 类不存在!");

}

}

public function getRedPackageByDTO(OptionDTO $optionDTO)

{

//获取策略

$builderStrategy = $this->getBuilderStrategy($optionDTO->builderStrategy);

//设置参数

$builderStrategy->setOption($optionDTO);

return $builderStrategy->create();

}

}

class Client

{

public static function main($argv)

{

//固定红包

$dto = OptionDTO::create(1000,10,100,100,'Equal');

$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);

//print_r($data);

//随机红包[修数据]

$dto = OptionDTO::create(5,10,0.01,0.99,'RandTriangle');

$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);

print_r($data);

//随机红包[不修数据]

$dto = OptionDTO::create(5,10,0.01,0.99,'RandTriangle','Can_Left');

$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);

//print_r($data);

}

}

$argv = [];

Client::main($argv);

微信红包随机数字_微信随机红包数详解和算法代码相关推荐

  1. 微信随机红包数详解和算法代码

    需求 CleverCode最近接到一个需求,需要写一个固定红包 + 随机红包算法. 1 固定红包就是每个红包金额一样,有多少个就发多少个固定红包金额就行. 2 随机红包的需求是.比如红包总金额5元,需 ...

  2. 微信红包随机数字_微信红包随机算法转载

    php固定红包 + 随机红包算法 1 需求 CleverCode最近接到一个需求,需要写一个固定红包 + 随机红包算法. 1 固定红包就是每个红包金额一样,有多少个就发多少个固定红包金额就行. 2 随 ...

  3. 微信红包随机数字_微信红包的随机算法

    概况:2014年微信红包使用数据库硬抗整个流量,2015年使用cache抗流量. 微信金额是拆的时候实时算出来,不是预先分配的,采用的是纯内存计算,不需要预算空间存储.采取实时计算金额的考虑:预算需要 ...

  4. 微信红包雨怎么抢_微信红包雨怎么发出? 微信红包雨发送技巧有哪些?

    微信红包雨怎么发出? 微信红包雨发送技巧有哪些?小编最近学会了一项技能,那就是发微信红包雨,一包多发,有对微信红包雨怎么发出感兴趣的伙伴吗,接下来小编与您分享下微信红包雨怎么发出吧. 微信红包雨怎么发 ...

  5. 微信链接修改图片_微信链接修改图标

    自定义链接是什么?微信链接修改图片_微信链接修改图标 自定义微信分享链接是指将一条网页链接通过微信接口生成一张卡片,并且该卡片的标题,内容和图片都可以自己编辑.如下图效果 ● 未自定义的网页链接 ● ...

  6. 随机森林随机回归预测_使用随机森林预测幸福

    随机森林随机回归预测 Why do we try to predict happiness? Being able to predict happiness means that we are abl ...

  7. imagepreview使用案例_微信小程序wx.previewImage预览图片实例详解

    一.小知识 二.例子 1.wxml 2.wxss .container { box-sizing:border-box; padding:20px; } .previewimg{ float:left ...

  8. 下拉多选框 微信小程序_微信小程序下拉框组件使用方法详解

    本文实例为大家分享了微信小程序下拉框组件的使用方法,供大家参考,具体内容如下 适用场景 1.省市三级联动 2.出生日期选择 3.性别选择 4.一般性的下拉选择等 一.省市三级联动使用 注意mode = ...

  9. “微信之父”张小龙首次演讲实录:详解微信平台四大价值观

    "微信之父"张小龙首次演讲实录:详解微信平台四大价值观(转) 1月11日上午,正在广州举行的"2016 微信公开课PRO版"上,被称为"微信之父&qu ...

最新文章

  1. 帝国cms7.5电脑端手机端多终端解决方案之选择哪种方案
  2. 用matlab画三维凸起,求助大牛MATLAB画三维等势面
  3. 微信公众号支付开发全过程(java版)
  4. STM32G071RB CubeMX ADC TIM DMA
  5. 转载:使用sklearn进行数据挖掘
  6. 【Python】Python3.7.3 - Python内置函数
  7. 【Elasticsearch】es 的 translog
  8. python批量从pdf中转换图片保存
  9. AUTOCAD二次开发VBA 基础到实例视频教程
  10. php++仿文库,仿百度文库源码商业版
  11. Layabox 1 layabox的项目文件以及项目配置
  12. oracle 新增配额,Oracle 用户配额
  13. 以读者角度走进RFID打造的智慧图书馆
  14. 白话大数据——大数据算法:白话遗传算法
  15. 免费阅读里的“B站”,后浪已乘“疯”
  16. javascript-按圆形排列DIV元素(一)---- 分析
  17. 【渝粤题库】陕西师范大学200551 英语语音
  18. trunk vlan pvid 学习实验整理 1
  19. 【Dest0g3 520迎新赛】密码学部分Wp及LCG(线性同余生成器)攻击方法
  20. 交叉编译libxml2

热门文章

  1. Scratch官方教程中文版(2)——制作可交互的生日贺卡
  2. CSS制作大风车动画及花朵
  3. 安卓内部存储之数据库存储
  4. 机器人坐标系基本概念
  5. http协议之response响应详解
  6. 计算机图形学实验报告 裁剪,计算机图形学-实验报告三-图形裁剪算法
  7. ehviewer_ehviewer1.7.7
  8. 20151023 - 用天枰3次从12个球中找1个不等重球的问题
  9. 使用 DHTML 与 XML 制作 Ajax 幻灯片
  10. 体育学校有没有计算机专业,职高有体育专业吗