You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Substrate的no_std环境下生成指定范围随机数

Substrate no_std环境下指定范围随机数实现方案

你已经通过RandomnessSource拿到了u64类型的基础随机数,只需要通过无偏差的范围映射处理即可实现指定范围的随机数生成,无需依赖rand库。

核心实现逻辑

不要直接使用random_number % range的简单模运算,会存在统计偏差:u64的最大值不是所有区间长度的整数倍,落在余数区间的数值会被多采样,严谨性不足。我们可以通过阈值校验剔除偏差部分:

/// 生成[0, range)范围内的无偏差u64随机数
fn get_bounded_random(random_raw: u64, range: u64) -> u64 {
    // 计算u64范围内可以被range整除的最大阈值
    let threshold = u64::MAX - (u64::MAX % range);
    let mut r = random_raw;
    // 剔除超出阈值的随机数避免偏差
    while r >= threshold {
        // 这里可以替换为用新nonce重新调用RandomnessSource取随机数
        r = sp_core::hashing::blake2_128(&r.encode())[0..8]
            .try_into()
            .map(u64::from_be_bytes)
            .unwrap();
    }
    r % range
}

如果需要生成[min, max)区间的随机数,只需要额外加偏移量即可:

let result = min + get_bounded_random(random_raw, max - min);

整合到现有业务代码的示例

fn draw_juror_for_citizen_profile_function(
        citizen_id: u128,
        length: usize,
    ) -> DispatchResult {
        // 边界校验:区间长度不能为0
        ensure!(length > 0, Error::<T>::InvalidJurorListLength);

        let nonce = Self::get_and_increment_nonce();
        let random_seed = T::RandomnessSource::random(&nonce).encode();
        let random_number = u64::decode(&mut random_seed.as_ref())
            .expect("secure hashes should always be bigger than u32; qed");
        
        // 生成0到length范围内的随机陪审员索引
        let juror_index = get_bounded_random(random_number, length as u64) as usize;

        // 后续业务逻辑
        // ...

        Ok(())
    }

注意事项

  • 如果业务对随机偏差容忍度很高,可以简化逻辑直接用random_number % range as u64,不需要阈值校验
  • RandomnessSource的随机安全性由你选择的实现决定,敏感业务建议结合自定义nonce、用户输入等多因子混合打散随机种子,避免随机数被预测
  • 超出阈值时的随机数刷新逻辑,你也可以替换为用nonce + 1重新调用RandomnessSource获取新种子

内容的提问来源于stack exchange,提问作者Amiya Behera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:36:08