<?php
/**
* 随机红包+固定红包算法[策略模式]
* copyright (c) 2016 http://blog.csdn.net/CleverCode
*/ //配置传输数据DTO
class RangeDto
{ //红包总金额
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(RangeDto $option); //是否可以生成红包
public function isCanBuilder(); //生成红包函数
public function fx($x);
} //固定等额红包策略
class EqualPackageStrategy implements IBuilderStrategy
{
//单个红包金额
public $oneMoney; //数量
public $num; public function __construct($option = null)
{
if ($option instanceof RangeDto) {
$this->setOption($option);
}
} public function setOption(RangeDto $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 RangeDto) {
$this->setOption($option);
}
} public function setOption(RangeDto $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; //我的峰值
$y2 = $this->maxMoney; //中间点
$x2 = ceil($this->num / 1.0 / 2); //最后点
$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);
} //我的峰值
$myMax = $this->maxMoney; // 如果还有余钱,则尝试加到小红包里,如果加不进去,则尝试下一个。
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 <= $myMax) {
$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(RangeDto $optionDTO)
{
//获取策略
$builderStrategy = $this->getBuilderStrategy($optionDTO->builderStrategy); //设置参数
$builderStrategy->setOption($optionDTO); return $builderStrategy->create();
} } class Client
{
public static function main()
{
//固定红包
$dto = RangeDto::create(1000, 10, 100, 100, 'Equal');
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
echo '<pre>';
print_r($data); //随机红包[修数据]
$dto = RangeDto::create(5, 10, 0.01, 0.99, 'RandTriangle');
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
echo '<pre>';
print_r($data); //随机红包[不修数据]
$dto = RangeDto::create(5, 10, 0.01, 0.99, 'RandTriangle', 'Can_Left');
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
echo '<pre>';
print_r($data); }
} Client::main();

php 固定红包 + 随机红包算法的更多相关文章

  1. 微信红包随机生成算法(PHP版)

    /** * 求一个数的平方 * @param $n */ function sqr($n){ return $n*$n; } /** * 生产min和max之间的随机数,但是概率不是平均的,从min到 ...

  2. PHP实现微信随机红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  3. php 随机红包算法

    <?php /** * 红包分配算法 * * example * $coupon = new Coupon(200, 5); * $res = $coupon->handle(); * p ...

  4. JS 红包随机

    微信随机红包,指定金额指定用户,随机发送红包 var moneys = new Array(); var moneyTotal = 0; function rand(obj){ if(obj.size ...

  5. python 生成随机红包

    假设红包金额为money,数量是num,并且红包金额money>=num*0.01 原理如下,从1~money*100的数的集合中,随机抽取num-1个数,然后对这些数进行排序,在排序后的集合前 ...

  6. Bagging与随机森林算法原理小结

    在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...

  7. R语言︱线性混合模型理论与案例探究(固定效应&随机效应)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 线性混合模型与普通的线性模型不同的地方是除了有 ...

  8. Python机器学习笔记——随机森林算法

    随机森林算法的理论知识 随机森林是一种有监督学习算法,是以决策树为基学习器的集成学习算法.随机森林非常简单,易于实现,计算开销也很小,但是它在分类和回归上表现出非常惊人的性能,因此,随机森林被誉为“代 ...

  9. 用Python实现随机森林算法,深度学习

    用Python实现随机森林算法,深度学习 拥有高方差使得决策树(secision tress)在处理特定训练数据集时其结果显得相对脆弱.bagging(bootstrap aggregating 的缩 ...

随机推荐

  1. MFMailComposeViewController发送邮件的实例

    本文转载至 http://blog.csdn.net/liufeng520/article/details/7585140   iPhone API已经提供了系统写邮件界面的接口,使用MFMailCo ...

  2. Oracle导出数据EXP00106错误

    在导出dmp文件的时候(命令:exp 用户名/密码@IP/实例名  file=D:\20180910.dmp log=D:\20180910.log),遇到以下错误: 错误原因: 导出使用的是Orac ...

  3. monit配置文件

    监控模式:(MONITRING MODE) Monit支持三种监控模式, active--Monitj监控一个服务,为了防止一系列问题,Monit会执行以及发送警报,停止,启动,重启,这是一个缺省的模 ...

  4. PHP异步请求之fsockopen()方法详解

    正常情况下,PHP执行的都是同步请求,代码自上而下依次执行,但有些场景如发送邮件.执行耗时任务等操作时就不适用于同步请求,只能使用异步处理请求. 场景要求: 客户端调用服务器a.php接口,需要执行一 ...

  5. Laravel 5.4设置logout注销账户的重定向路径

    当我们修改Laravel默认Auth默认路径时,在点击logout按钮注销时,默认跳转的地址为项目的根目录, 若想设置成自定义的重定向路径,可以按照如下设置: 方法一: 在Auth \ LoginCo ...

  6. spring boot中的jave注解学习

    在spring中,不仅框架作者会使用java注解,开发者也常使用. 可以随手给个例子:在org.springframework.boot.autoconfigure.jdbc.DataSourcePr ...

  7. js 判断js,css是否引入,确保不重复引入

      基本原理:function loadjscssfile(filename, filetype){if (filetype=="js"){ //if filename is a ...

  8. MUI学习01-顶部导航栏

    建议:先看一下MUI注意事项 连接:http://ask.dcloud.net.cn/article/122 固定栏靠前 所谓的固定栏,也就是带有.mui-bar属性的节点,都是基于fixed定位的元 ...

  9. ES6(六) --- Symbol

    概述: ES5 中属性名都是字符串,这容易就造成命名的冲突,特别是在混入模式(mixin模式)下.为解决这个问题ES6 引入了Symbol, Symbol是一种新的基本数据类型,表示独一无二的值!   ...

  10. 常用C++面试总结

    指定对齐值:#pragma pack(n),n=1,2,4,8,16改变系统的对齐系数struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成 ...