大数跨境

带库存限制的权重抽奖算法

带库存限制的权重抽奖算法 wordpress知识
2026-10-17
18

本文分享适用于公众号抽奖、H5大转盘及小程序扭蛋活动的带库存限制权重抽奖算法。

  • 支持为不同奖品设置差异化中奖概率(权重)
  • 每个奖品独立管理库存,库存耗尽后自动从候选池剔除
  • 采用密码学安全随机数生成器,规避普通随机函数易被预测及刷奖的风险

算法原理详解

以以下配置为例:

  • 手机:权重 1,库存 2
  • 优惠券:权重 20,库存 50
  • 谢谢参与:权重 300,库存 99999

计算逻辑:

  1. 过滤无效奖品:每次抽奖前,先剔除库存为 0 的奖品。
  2. 计算总权重:将剩余所有有库存奖品的权重相加。例如初始总权重 = 1 + 20 + 300 = 321。
  3. 生成随机数:在 `1 ~ 总权重` 区间内生成一个安全随机数。
  4. 区间匹配:按顺序累加奖品权重,随机数落在哪个区间即命中对应奖品。
    • 随机数 = 1 → 中手机
    • 随机数 2~21 → 中优惠券
    • 随机数 22~321 → 谢谢参与
  5. 动态调整:若手机库存抽完归零,下次抽奖时手机被过滤,总权重变为 320,区间自动重新计算。

方案优缺点分析

优点:

  • 概率可控,可通过低权重设置大奖稀缺性。
  • 库存严格锁定,杜绝超发,预算可控。
  • 实时出结果,适配大转盘等即时交互场景。
  • 使用 `random_int()` 实现密码级安全随机,安全性远高于 `mt_rand`。

缺点:

  • 属于即时概率抽奖,不支持事后验算,公信力弱于哈希种子抽签。
  • 无法保证固定人数内必然产出固定数量的大奖,仅符合概率分布。

注:若需求为报名结束后统一开奖且需公示验真,建议改用哈希公平抽签算法。

PHP 核心代码实现

<?php
/**
 * 带库存控制的权重抽奖算法
 * 适用:公众号H5大转盘、小程序抽奖、扭蛋机、即时抽奖
 * @param array $prizeList 奖品数组,结构要求:name奖品名称, weight权重, stock库存
 * @return array 中奖奖品信息
 */
function weightDrawWithStock(array $prizeList): array
{
    // 第一步:过滤库存为0的奖品,只保留还有库存的奖品
    $validPrize = [];
    $totalWeight = 0;
    foreach ($prizeList as $item) {
        // 必须同时存在三个字段,并且库存大于0才进入候选池
        if (isset($item['name'], $item['weight'], $item['stock']) && $item['stock'] > 0) {
            $validPrize[] = $item;
            $totalWeight += $item['weight'];
        }
    }
    
    // 没有任何可抽奖品
    if ($totalWeight <= 0 || empty($validPrize)) {
        return [
            'name' => '奖品已全部抽完',
            'weight' => 0,
            'stock' => 0
        ];
    }
    
    // 第二步:密码安全随机,生成 1 ~ 总权重 的随机数
    $randNum = random_int(1, $totalWeight);
    
    // 第三步:区间遍历,命中奖品
    $currentRange = 0;
    foreach ($validPrize as $prize) {
        $currentRange += $prize['weight'];
        if ($randNum <= $currentRange) {
            // 命中该奖品,直接返回
            return $prize;
        }
    }
    
    // 兜底返回:谢谢参与
    return [
        'name' => '谢谢参与',
        'weight' => 0,
        'stock' => 0
    ];
}

// ===================== 测试示例 =====================
// 奖品配置,这里可以直接从数据库读取
$prizeConfig = [
    [
        'name' => '品牌手机',
        'weight' => 1,
        'stock' => 2
    ],
    [
        'name' => '10元无门槛优惠券',
        'weight' => 20,
        'stock' => 50
    ],
    [
        'name' => '谢谢参与',
        'weight' => 300,
        'stock' => 99999
    ]
];

// 执行一次抽奖
$winResult = weightDrawWithStock($prizeConfig);
echo "本次抽到奖品:" . $winResult['name'];

关键注意事项与避坑指南

上述函数仅负责判定中奖结果,实际开发中需注意以下核心问题:

1. 并发控制:必须使用数据库事务

抽奖与扣库存操作必须包裹在数据库事务中。在高并发场景下(如数百人同时点击),若不添加事务及行锁,可能导致多人同时读到同一剩余库存(如库存=1),均命中大奖,最终导致库存扣减为负数。

解决方案:使用 InnoDB 引擎,在确定中奖瞬间对奖品记录加行锁。

2. 高频踩坑清单

  • 禁用伪随机函数:`mt_rand` / `rand` 种子可预测,极易被技术手段批量刷奖。生产环境务必使用 PHP7+ 原生支持的 `random_int()`。
  • 严禁异步扣库存:高并发下异步处理必然导致超发。必须同步完成“判断中奖”与“扣减库存”。
  • “谢谢参与”需设大库存:避免将所有实物奖品抽完后程序报错。建议将“谢谢参与”设为极大库存值(如 99999)作为保底。
  • 权重数值合理:权重总和应避免超出 PHP 整数范围,一般业务场景中几百至几千的权重值已足够,无需设置过大数据。
  • 后端主导逻辑:绝对禁止在前端 JS 中编写抽奖算法。前端仅负责动画展示,结果判定必须由后端完成,防止通过 F12 篡改代码刷奖。

3. 扩展优化建议

  • 频次限制:在用户表或独立表中记录每日抽奖次数,限制单用户行为。
  • 黑名单机制:识别并拦截恶意刷奖账号。
  • 防刷策略:增加 IP 限制、验证码校验及接口请求冷却时间,抵御脚本攻击。
  • 分时段概率(慎用):虽可调整不同时段的大奖权重,但容易引发用户关于公平性的质疑,需谨慎使用。
【声明】内容源于网络
0
0
wordpress知识
各类跨境出海行业相关资讯
内容 376
粉丝 0
wordpress知识 各类跨境出海行业相关资讯
总阅读13.1k
粉丝0
内容376