Python基于指定统计分布生成带上下限范围随机数的方法
限定上下界的指定分布随机数生成方案(Python)
所有定义在无界区间或[0,1]标准区间的概率分布,都可以通过截断+线性映射的方式适配任意自定义上下限,不需要手动枚举区间值、也不需要手写权重数组,完全适配2000000~1800000000微秒的取值区间要求。
前置依赖
推荐用numpy实现批量/单个样本生成,比原生random模块效率高1~2个数量级,需要严格正态分布场景可搭配scipy使用:
pip install numpy scipy
首先定义全局区间常量,后续所有分布函数直接复用:
import numpy as np from scipy.stats import truncnorm # 区间上下限,单位:微秒 LOWER_BOUND = 2_000_000 UPPER_BOUND = 1_800_000_000 INTERVAL_RANGE = UPPER_BOUND - LOWER_BOUND
各目标分布实现
1. Beta分布(最灵活,优先推荐)
Beta分布原生取值范围固定为[0,1],不需要额外做截断处理,直接线性映射到目标区间即可。仅调整Alpha、Beta两个参数即可生成各类对称、偏态、U型分布形态:
alpha=beta=1:等价于均匀分布,和random.randint效果一致alpha=beta>1:对称钟形分布(类正态),参数值越大分布越窄,越向区间中点集中alpha>beta:右偏(负偏)分布,峰值靠近区间上限beta>alpha:左偏(正偏)分布,峰值靠近区间下限alpha<1, beta<1:U型分布,概率集中在区间两端
实现代码:
def sample_beta(alpha: float, beta: float, size: int = 1) -> int | np.ndarray: raw_samples = np.random.beta(alpha, beta, size=size) mapped = (LOWER_BOUND + raw_samples * INTERVAL_RANGE).round().astype(np.int64) return mapped[0] if size == 1 else mapped # 调用示例 left_skew_num = sample_beta(alpha=2, beta=5) # 左偏(正偏),小值概率更高 narrow_norm_num = sample_beta(alpha=10, beta=10) # 窄对称钟形分布,集中在区间中点 wide_norm_num = sample_beta(alpha=2, beta=2) # 宽对称钟形分布,取值更分散 right_skew_num = sample_beta(alpha=5, beta=2) # 右偏(负偏),大值概率更高
2. 截断正态分布(严格正态特性)
如果需要严格符合正态分布的概率密度特性(而非Beta类钟形近似),使用截断正态分布,自动丢弃超出上下限的样本,保证所有返回值都在目标区间内:
def sample_trunc_normal(mean: float, std: float, size: int = 1) -> int | np.ndarray: # 计算上下限对应的标准化分位数 a = (LOWER_BOUND - mean) / std b = (UPPER_BOUND - mean) / std samples = truncnorm.rvs(a, b, loc=mean, scale=std, size=size) mapped = samples.round().astype(np.int64) return mapped[0] if size == 1 else mapped # 调用示例 mid_point = (LOWER_BOUND + UPPER_BOUND) / 2 narrow_norm_num = sample_trunc_normal(mean=mid_point, std=INTERVAL_RANGE/10) # 窄正态 wide_norm_num = sample_trunc_normal(mean=mid_point, std=INTERVAL_RANGE/3) # 宽正态
3. 指数分布/逆指数分布
指数分布原生取值范围为[0, +∞),通过逆变换采样截断到[0,1]区间后映射到目标范围即可:
- 普通指数分布:取值越小概率越高,靠近2秒的样本占比最高
- 逆指数分布:取值越大概率越高,靠近30分钟的样本占比最高
实现代码:
def sample_exponential(lam: float = 2, size: int = 1) -> int | np.ndarray: u = np.random.uniform(size=size) raw = -np.log(1 - u * (1 - np.exp(-lam))) / lam mapped = (LOWER_BOUND + raw * INTERVAL_RANGE).round().astype(np.int64) return mapped[0] if size == 1 else mapped def sample_inverse_exponential(lam: float = 2, size: int = 1) -> int | np.ndarray: u = np.random.uniform(size=size) raw = 1 + np.log(u * (1 - np.exp(-lam)) + np.exp(-lam)) / lam mapped = (LOWER_BOUND + raw * INTERVAL_RANGE).round().astype(np.int64) return mapped[0] if size == 1 else mapped # 调用示例 exp_num = sample_exponential() # 指数分布 inv_exp_num = sample_inverse_exponential() # 逆指数分布
补充说明
- 如果需要纯Python实现(不依赖numpy/scipy),将代码中numpy的随机数调用替换为
random模块对应方法即可,仅在批量生成百万级以上样本时速度会明显降低 - 所有函数默认返回整数微秒值,如果需要浮点精度,去掉
.round().astype(np.int64)转换即可 - 单样本调用时默认返回单个int值,需要批量生成时传入
size参数即可返回对应长度的数组,不需要循环调用
内容的提问来源于stack exchange,提问作者Sal
相关产品推荐
相关产品推荐

