123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- <?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);
|