如何在Python中生成区间[a,b]内总和接近k的n个随机数?
解决方案
要生成26个范围在[100,120]的整数,使其总和落在[3000,3050]区间内,同时保持随机分布,可以按照以下步骤实现:
核心思路
- 随机选定目标总和(在3000-3050区间内)
- 给每个数分配基础值100,计算需要额外补充的额度
- 将额外额度随机分配到各个数上,确保每个数最终不超过120
- 验证结果并可视化分布质量
完整代码实现
import numpy as np import scipy.stats as stats import matplotlib.pyplot as plt # 设定固定参数 n_days = 26 min_daily = 100 max_daily = 120 target_min = 3000 target_max = 3050 # 1. 随机选择目标总和 target_sum = np.random.randint(target_min, target_max + 1) # 2. 计算基础总和与待分配的额外额度 base_sum = n_days * min_daily remaining = target_sum - base_sum # 3. 用多项式分布随机分配额外额度 additions = np.random.multinomial(remaining, [1/n_days]*n_days, size=1)[0] # 防御性调整:处理极端情况中单个数值超上限的问题 max_add = max_daily - min_daily while any(additions > max_add): # 收回超标的额度 excess_mask = additions > max_add excess = additions[excess_mask] - max_add additions[excess_mask] = max_add remaining_excess = excess.sum() # 将收回的额度重新分配给未达上限的元素 available_mask = additions < max_add additions[available_mask] += np.random.multinomial(remaining_excess, [1/available_mask.sum()]*available_mask.sum(), size=1)[0] # 生成最终的每日收入数组 daily_values = min_daily + additions # 验证结果 total = daily_values.sum() print(f"生成的每日收入:{daily_values}") print(f"总和:{total},符合目标区间:{target_min} ≤ {total} ≤ {target_max}") # 4. 可视化分布(对比正态分布PDF) sorted_vals = np.sort(daily_values) mean_val = np.mean(daily_values) std_val = np.std(daily_values) plt.figure(figsize=(10,6)) plt.hist(daily_values, bins=range(min_daily, max_daily+2), edgecolor='black', alpha=0.7, label='生成值分布') plt.plot(sorted_vals, stats.norm.pdf(sorted_vals, mean_val, std_val)*len(daily_values), '-o', alpha=0.6, label='拟合正态分布') plt.xlabel('每日收入(€)') plt.ylabel('频次') plt.title('每日收入分布对比') plt.legend() plt.show()
代码说明
- 目标总和选择:随机选取3000-3050之间的整数,确保总和直接符合要求
- 额度分配:使用
numpy.random.multinomial实现额外额度的随机分配,保证数值分布的随机性 - 防御性调整:处理极端情况下可能出现的单个数值超上限问题,确保结果合法性
- 分布可视化:将生成值的直方图与拟合的正态分布对比,验证分布的合理性
简化替代方案(快速生成)
如果不需要严格精确的目标总和,也可以先生成初始随机数,再逐步调整总和到目标区间:
import numpy as np n_days = 26 min_daily = 100 max_daily = 120 target_min = 3000 target_max = 3050 # 生成初始随机数(注意randint左闭右开,上限写121才能包含120) daily_values = np.random.randint(min_daily, max_daily + 1, n_days) current_sum = daily_values.sum() # 循环调整总和到目标区间 while not (target_min <= current_sum <= target_max): idx = np.random.randint(n_days) if current_sum < target_min: if daily_values[idx] < max_daily: daily_values[idx] += 1 current_sum += 1 else: if daily_values[idx] > min_daily: daily_values[idx] -= 1 current_sum -= 1 print(f"调整后总和:{current_sum}") print(f"每日收入:{daily_values}")
这个方案逻辑简单,适合对生成效率要求不高的场景。
内容的提问来源于stack exchange,提问作者Mario
相关产品推荐
相关产品推荐

