微信红包随机数字_微信随机红包数详解和算法代码
1 需求
寫一個固定紅包 + 隨機紅包
固定紅包就是每個紅包金額一樣,有多少個就發(fā)多少個固定紅包金額就行。
隨機紅包的需求是。比如紅包總金額5元,需要發(fā)10個紅包。隨機范圍是 0.01到0.99;5元必需發(fā)完,金額需要有一定趨勢的正態(tài)分布。(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 隨機紅包
如果我們使用隨機函數(shù)rand。rand(0.01,0.99);那么10次隨機,如果最壞情況都是金額0.99,總金額就是9.9元。會超過5元。金額也會不正態(tài)分布。最后思考了一下借助與數(shù)學函數(shù)來當作隨機紅包的發(fā)生器,可以用拋物線,三角函數(shù)。最后選定了等腰三角線性函數(shù)。
1 算法原理
如果需要發(fā)紅包總金額是totalMoney,紅包個數(shù)是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)
修數(shù)據(jù):
y(合) = y1 + y2 + y3 +...... ynum;
y(合)有可能 > totalMoney ,說明生成金額多了,需要修數(shù)據(jù),則從(y1,y2,y3.....ynum)這些每次減少0.01。直到y(tǒng)(合) = totalMoney。
y(合)有可能 < totalMoney ,說明生成金額少了,需要修數(shù)據(jù),則從(y1,y2,y3.....ynum)這些每次加上0.01。直到y(tǒng)(合) = totalMoney。
2 算法原理樣例
如果需要發(fā)紅包總金額是11470,紅包個數(shù)是7400個,金額范圍是[0.01,3.09],線性方程如圖。
image.png
3 需求設計
3.1 類圖設計
image.png
3.2 源碼設計
/**
* 隨機紅包+固定紅包算法[策略模式]
* copyright (c) 2016 http://blog.csdn.net/CleverCode
*/
//配置傳輸數(shù)據(jù)DTO
class OptionDTO
{
//紅包總金額
public $totalMoney;
//紅包數(shù)量
public $num;
//范圍開始
public $rangeStart;
//范圍結算
public $rangeEnd;
//生成紅包策略
public $builderStrategy;
//隨機紅包剩余規(guī)則
public $randFormatType; //Can_Left:不修數(shù)據(jù),可以有剩余;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
{
//創(chuàng)建紅包
public function create();
//設置配置
public function setOption(OptionDTO $option);
//是否可以生成紅包
public function isCanBuilder();
//生成紅包函數(shù)
public function fx($x);
}
//固定等額紅包策略
class EqualPackageStrategy implements IBuilderStrategy
{
//單個紅包金額
public $oneMoney;
//數(shù)量
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;
//紅包數(shù)量
public $num;
//隨機紅包最小值
public $minMoney;
//隨機紅包最大值
public $maxMoney;
//修數(shù)據(jù)方式: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;
}
/**
* 創(chuàng)建隨機紅包
*
* @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];
}
//修數(shù)據(jù)
list($okLeftMoney,$okData) = $this->format($leftMoney,$data);
//隨機排序
shuffle($okData);
$this->leftMoney = $okLeftMoney;
return $okData;
}
/**
* 是否能夠發(fā)隨機紅包
* @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;
}
/**
* 隨機紅包生成函數(shù)。三角函數(shù)。[(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;
}
/**
* 格式化修紅包數(shù)據(jù)
*
* @param mixed $leftMoney
* @param array $data
* @access public
* @return void
*/
private function format($leftMoney,array $data)
{
//不能發(fā)隨機紅包
if(false == $this->isCanBuilder())
{
return array($leftMoney,$data);
}
//紅包剩余是0
if(0 == $leftMoney) //無需修數(shù)據(jù)
{
return array($leftMoney,$data);
}
//數(shù)組為空
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)
{
//減少循環(huán)優(yōu)化
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,'.','');
}
}
//如果沒有可以加的紅包,需要結束,否則死循環(huán)
//也就是會出現(xiàn)每個紅包不分錢的情況,比如紅包都已經(jīng)最大值。這時必須在分的時候給予標志,防止死循環(huán)。
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,'.','');
}
}
//如果一個減少的紅包都沒有的話,需要結束,否則死循環(huán)
if($found == 0)
{
break;
}
}
return array($leftMoney,$data);
}
}
//維護策略的環(huán)境類
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);
//設置參數(shù)
$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);
//隨機紅包[修數(shù)據(jù)]
$dto = OptionDTO::create(5,10,0.01,0.99,'RandTriangle');
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
print_r($data);
//隨機紅包[不修數(shù)據(jù)]
$dto = OptionDTO::create(5,10,0.01,0.99,'RandTriangle','Can_Left');
$data = RedPackageBuilder::getInstance()->getRedPackageByDTO($dto);
//print_r($data);
}
}
$argv = [];
Client::main($argv);
總結
以上是生活随笔為你收集整理的微信红包随机数字_微信随机红包数详解和算法代码的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【复习总结】玫瑰人生 La Vie en
 - 下一篇: android 开红包动画,Androi