不败君

前端萌新&初级后端攻城狮

PHP实现微信红包金额拆分及原理解析

PHP实现微信红包金额拆分及原理解析

2020-01-08 19:32:01

围观(2975)

微信红包有个好玩的地方,比如可以将 10 元放入红包,设置红包数量为 10 个.然后系统会自动将这 10 元拆分,有的人可能领到了几块钱,而有的人就只领到了几分钱.


实现方法:

function getRedPacket($price, $total = 10)
{
    $min_price = 0.01;
    $temp = [];
    $return = [];
    for ($i = 1; $i < $total; ++$i) {
        // 红包金额的最大值
        $max_total = ($price - ($total - $i) * $min_price) / ($total - $i);
        if ($max_total < 0) break;
        // 随机产生一个红包金额
        $money = @mt_rand($min_price * 100, $max_total * 100) / 100;
        // 剩余红包总额
        $price = $price - $money;
        // 保留两位有效数字
        $temp[$i] = round($money, 2);
    }

    $temp[$i] = round($price, 2);
    $return['money'] = $temp;
    $return['total'] = array_sum($temp);
    return $return;
}


调用该方法,可以得到这样的数据(实际返回的是数组 json 格式需要自己转):

{
    "money": {
        "1": 1.06,
        "2": 0.01,
        "3": 0.45,
        "4": 1.28,
        "5": 0.91,
        "6": 0.7,
        "7": 1.79,
        "8": 1.87,
        "9": 1,
        "10": 0.93
    },
    "total": 10
}


其实实现方法不难,重点是代码中的这句:

$max_total = ($price - ($total - $i) * $min_price) / ($total - $i);

十个十元的红包,循环九次执行这个算法之后,得到的结果是:

(10 - (10 - 1) *0.01) / (10 - 1)    得出结果    1.1011111111111
(9.45 - (10 - 2) *0.01) / (10 - 2)  得出结果    1.17125
(9.3 - (10 - 3) *0.01) / (10 - 3)   得出结果    1.3185714285714
(9.06 - (10 - 4) *0.01) / (10 - 4)  得出结果    1.5
(7.72 - (10 - 5) *0.01) / (10 - 5)  得出结果    1.534
(6.91 - (10 - 6) *0.01) / (10 - 6)  得出结果    1.7175
(6.62 - (10 - 7) *0.01) / (10 - 7)  得出结果    2.1966666666667
(6.29 - (10 - 8) *0.01) / (10 - 8)  得出结果    3.135
(5.45 - (10 - 9) *0.01) / (10 - 9)  得出结果    5.44

可以看出,该算法就是: ( 剩余的金额 - ( 总金额 - 次数 ) * 0.01 ) / ( 总金额 - 次数 ) 

第一次循环时,剩余的金额是 10 因为总金额就 10 ,然后经过计算得出的结果是 1.1011111111111 如果传的是 10 元 分成 10 个,那么第一次的结果总是一致的.


拿到了第一个结果,就可以根据最低金额(0.01 一分钱),和刚才计算出来的金额,进行生成随机数.


比如生成一个随机数是 0.53 ,就将这个数存入数组,因为这个数就是第一个红包的金额了,存的时候注意下保留两位小数.

说一下,这里将最低金额 0.01 和 最大金额(算法算出的金额) 进行乘 100 计算, 算完再除 100 ,原因是为了对 "分" 做到更加精确,比如写 微信/支付宝/银联 的支付开发,支付金额都是用单位为分的金额传输的.


得到了第一个红包的金额,就可以将总金额 减 刚才得到的随机数(第一个红包),就得到了目前的剩余总金额,然后一直还需要循环八次,重复执行得到九个红包的金额.


可能有人会问,还有最后一个红包金额呢? 其实很简单,你都得到了九个红包了,并且每次执行代码都会得到一个 "目前的红包总额", 那第十个红包不就是这个总额了...


所以,根据上面的代码可以拆分红包,但是... 基本上最后一个红包是最大的,比如拆分三次的结果:

{
    "money": {
        "1": 0.73,
        "2": 0.6,
        "3": 0.89,
        "4": 0.47,
        "5": 0.65,
        "6": 0.2,
        "7": 0.37,
        "8": 0.94,
        "9": 1.76,
        "10": 3.39
    },
    "total": 10
}
{
    "money": {
        "1": 0.53,
        "2": 1.09,
        "3": 0.61,
        "4": 0.42,
        "5": 0.8,
        "6": 0.99,
        "7": 0.06,
        "8": 2.44,
        "9": 0.84,
        "10": 2.22
    },
    "total": 10
}
{
    "money": {
        "1": 0.22,
        "2": 0.16,
        "3": 0.86,
        "4": 1.28,
        "5": 0.58,
        "6": 1.52,
        "7": 1.13,
        "8": 1.62,
        "9": 0.76,
        "10": 1.87
    },
    "total": 10
}


博主并没有特意把第十个红包最大的数据拿出来说,而是三次执行代码,拿到的数据就是这样的.

都是第十个(最后一个)红包的金额最大,如果介意,可以使用这个函数把数组值打乱:

shuffle(array &array);

比如这样调用:

$data = getRedPacket(10, 10);

// 以下数据没有打乱 基本上第十个红包是最大的
echo json_encode($data);

// 进行打乱数据
shuffle($data['money']);
// 再次输出的数据 和第一次输出的数据顺序就不一样了
echo json_encode($data);


本文地址 : bubaijun.com/page.php?id=161

版权声明 : 未经允许禁止转载!

评论:我要评论
发布评论:
Copyright © 不败君 粤ICP备18102917号-1

不败君

首 页 作 品 微 语