一个简单的发红包方法

在这里写了一个简单的产生金额的方法,代码如下:

/**
 * 根据红包总金额,个数,类型去产生红包金额数组
 * @param int $totalValue 红包总金额 单位为分
 * @param int $number 红包个数
 * @param int $type 红包类型 0为随机手气红包 1为平分红包 默认为0
 * @param int $min 每个红包的最小金额 单位为分 默认为1
 * @return array|bool false为参数错误 数组为红包的金额数组 每个元素为int型数值 单位为分
 */
function getBags(int $totalValue, int $number, int $type = 0, int $min = 1)
{
    //总金额和红包个数校验
    if (($totalValue < 1) || ($number < 1)
    ) {
        return false;
    }
    //总金额应能够满足最小金额
    if ($totalValue < $number * $min) {
        return false;
    }
    //一个红包
    if ($number == 1) {
        return [$totalValue];
    }
    $result = [];
    $usedBags = $lastBag = 0;
    //随机手气红包: 产生$number-1个随机数,将0-$totalValue分割成$number份,每一份就是一个红包
    if ($type == 0) {
        $randPoints = [];
        //每一个随机数值的下限是上一个数值+1, 上限是总金额-预留金额, 以确保后面的每个红包金额不小于最小金额
        //第一个随机数值的下限是最小金额, 上限是总金额减去$number-1个最小金额
        $remainingValue = $totalValue - (($number-1) * $min);
        $latestRandPoint = mt_rand($min, $remainingValue);
        $randPoints[] = $latestRandPoint;
        $result[] = $latestRandPoint;
        $usedBags += $latestRandPoint;
        //第二个到最后一个随机数值
        for ($i=1; $i<$number-1; $i++) {
            $remainingValue += $min;
            $latestRandPoint = mt_rand($latestRandPoint+1, $remainingValue);
            $randPoints[] = $latestRandPoint;
            //当前红包的金额 = 当前随机值 - 上一个随机值
            $bag = $latestRandPoint - $randPoints[$i-1];
            $result[] = $bag;
            $usedBags += $bag;
        }
    } else { //平分红包
        $times = $number;
        while ($times-1) {
            $bag = round($totalValue/$number, 0);
            $result[] = $bag;
            $usedBags += $bag;
            $times--;
        }
    }
    //最后一个红包
    $lastBag = $totalValue - $usedBags;
    $result[] = $lastBag;
    return $result;
}

测试 (注意金额单位是分)

$result = getBags(1000, 5, 0);
var_dump($result);

输出
array(5) { [0]=> int(25) [1]=> int(199) [2]=> int(379) [3]=> int(137) [4]=> int(260) }  

这里的核心思想,就是 把总金额$totalValue看成一个从0出发,总长度为总金额的区间,然后在(0, $totalValue) 这个区间中,找出 $number - 1 个随机浮点数,将这个区间或者线段,分隔成 $number 部分,每一个部分的长度,就是一个红包的金额大小。

时间复杂度是O(n)。

这里只提供一种分配的思路,没有考虑到兼顾"公平"的策略,比如限制某个红包最大金额之类的。所以有可能出现5个人分10块钱的红包,有四个人都是最小金额0.01,另一个人9.96这种极端情况。至于"公平性",就涉及到一些更复杂的算法策略了。