您好,欢迎访问一九零五行业门户网

php如何实现固定红包以及随机红包算法详解(图)

1 需求clevercode最近接到一个需求,需要写一个固定红包 + 随机红包算法。
1 固定红包就是每个红包金额一样,有多少个就发多少个固定红包金额就行。
2 随机红包的需求是。比如红包总金额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)
2.2 随机红包如果我们使用随机函数rand。rand(0.01,0.99);那么10次随机,如果最坏情况都是金额0.99,总金额就是9.9元。会超过5元。金额也会不正态分布。最后思考了一下借助与数学函数来当作随机红包的发生器,可以用抛物线,三角函数。最后选定了等腰三角线性函数。
1 算法原理
如果需要发红包总金额是totalmoney,红包个数是num个,金额范围是[min,max],线性方程如图。
三个点的坐标:
(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],线性方程如图。
3 需求设计3.1 类图设计
3.2 源码设计<?php /** * 随机红包+固定红包算法[策略模式] * 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; //我的峰值 $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(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); } }/*}}}*/ client::main($argv);
3.3 结果展示 1 固定红包
//固定红包 $dto = optiondto::create(1000,10,100,100,'equal'); $data = redpackagebuilder::getinstance()->getredpackagebydto($dto); print_r($data);
2 随机红包(修数据)
这里使用了php的随机排序函数, shuffle($okdata),所以看到的结果不是线性的,这个结果更加随机性。
//随机红包[修数据] $dto = optiondto::create(5,10,0.01,0.99,'randtriangle'); $data = redpackagebuilder::getinstance()->getredpackagebydto($dto); print_r($data);
3 随机红包(不修数据)
不修数据,1 和num的金额是最小值0.01。
//随机红包[不修数据] $dto = optiondto::create(5,10,0.01,0.99,'randtriangle','can_left'); $data = redpackagebuilder::getinstance()->getredpackagebydto($dto); print_r($data);
以上就是php如何实现固定红包以及随机红包算法详解(图)的详细内容。
其它类似信息

推荐信息