Python中如何生成10个和为100的[0,100]随机数?
感谢大家的帮助,看了大家的回复后感觉确实有点像微信红包的算法,但是红包是不包含 0 的。另外,我也感觉标题中的“随机”这个表述有问题,应该是 [从 [0,100] 取 10 个数,使之和等于 100,可取 0 和 100 ] 不知道这次表述是否合适了。
Python中如何生成10个和为100的[0,100]随机数?
mark 我也想知道
import random
def generate_random_numbers():
"""
生成10个和为100的[0,100]随机数
使用拒绝采样法确保每个数都在指定范围内
"""
while True:
# 生成9个随机数作为分割点
splits = sorted(random.sample(range(1, 100), 9))
# 计算相邻分割点之间的差值作为随机数
numbers = [splits[0]]
for i in range(1, 9):
numbers.append(splits[i] - splits[i-1])
numbers.append(100 - splits[-1])
# 检查所有数是否都在[0,100]范围内
if all(0 <= num <= 100 for num in numbers):
return numbers
# 生成并验证结果
result = generate_random_numbers()
print(f"生成的随机数: {result}")
print(f"数量: {len(result)}")
print(f"总和: {sum(result)}")
print(f"范围检查: {all(0 <= num <= 100 for num in result)}")
# 多次运行验证
print("\n验证运行5次:")
for _ in range(5):
test_result = generate_random_numbers()
print(f"总和={sum(test_result)}, 范围={min(test_result)}~{max(test_result)}")
核心思路:
- 在1-99之间随机选择9个分割点
- 计算相邻分割点(包括0和100)的差值得到10个随机数
- 通过while循环确保所有数都在[0,100]范围内
算法特点:
- 每个数都是均匀随机生成的
- 保证总和严格等于100
- 每个数都在[0,100]区间内
- 时间复杂度O(1),因为最多几次重试就能得到有效结果
一句话建议:用分割区间法生成随机数既简单又保证分布均匀。
这就不叫随机数吧…
而且最简单的方法就是随机生成,然后总和 /100 = k,每个数再 /k
在一根 1 到 100 的数轴上,随机取 9 个点,拿到 10 个线段。计算每个线段的长度,即是取值。
关注
牛逼
#3 稳了,反正没要求是整数
有个思路是先在一个范围里随机取 10 个数,然后 sum,然后缩放到 100,然后做点去重的偏移。
包含 0 和 100,可以重复,需要是整数
在马桶上随便写写,没怎么仔细想,各位见笑
伪代码
func(nums: array = [0])
if i=nums.length < 11
nums[i] = rand(nums[i-1], 100)
return func(nums)
nums.removeAt(0)
return nums
func()
但是严格意义上讲,虽然数列中的每个元素都是随机函数生成的,但是这个数列并不是随机数列
#6 不含 0 是 (0, 100]
递减呗
这根本不是随机数
照着 思路
points = [random.randint(0, 100) for i in range(9)] # 生成 9 个随机点
points = [0] + sorted(points) + [100] # 排个队
points = [points[i + 1] - points[i] for i in range(10)] # 算每段距离
print(points, sum(points))
忽略我上面的答案,没认真审题…
那就生成 9 个随机数,范围看你需求
最后一个 100 - 9 个和 不就好了。
#17 你这样分布可能不够分散
生成 9 个随机数,然后用 100 减去 9 个数的和作为第十个数
第一次取 0 到 100 第二次取 0 到 100 减第一个数
凡是描述概率问题的时候不给分布瞎答就可以了
$数组 = 1…100
while ($true)
{
$a,$b,$c,$d,$e,$f,$g,$h,$i,$j = Get-Random -InputObject $数组 -Count 10
$和 = $a + $b + $c + $d + $e + $f + $g + $h + $i + $j
if ($和 -lt 120)
{
Write-Host “$a $b $c $d $e $f $g $h $i $j $k”
}
}
上面的 powershell 代码,设定值小于 120 时 ,5 分钟没出结果。
我估计 -eq 100 得一年才能出结果。而设定 -lt 200 很好出结果,大家可以试试。
生成一个[0, 100] 的随机数,第二个数范围是 [0, 100-num1],第三个是[0, 100-num1-num2],以此类推?
你这个不能叫随机数,近似使用可以 10+randint(-10,10)
很有意思的问题,有很多实现方式,随着限制条件(去重,整数,负数)的增加,变数更多,很适合作为面试口答题哦。
生成 9 个,最后一个用 100 减去🌚🌚🌚
Python 没学过,用 js 实现一个
var arr=new Array(10);
for(let i=0; i<10; i++){
arr[i]=0;
}
for(let i=0; i<100; i++){
var index=Math.floor(Math.random()*10);
arr[index]++
}
3 楼正解,取 9 个点然后拿到十个线段长度即可,如果需要整数,可以规定随机到的九个点都是整数即可
random 取整数,加和,映射到 100 内( 100/加和总数*十个数总值)
结果:前九个数+0.5 取整
最后一个数 100-前九个数的合
这个问题就是微信红包是怎么随机分配的吧
10 个数和为 100,均值也就是 10,也就是说最好在 0-20 里面取值。
而且绝对不能取到大于 91 的数,92-100 已经被排除在外了
这种条件,还能叫随机吗
https://gist.github.com/fei-ke/2e86bce8aa52bff5d0bf
好多年前写过一个微信红包的算法( Java 的),可以拿去参考一下
直接 100 落概率桶,总和为 10,难道还有负数?
关键在于随机数不一定要同时生成吧
第一个数 n[0] = rand(0, 100);
第二个数 n[1] = rand(0, 100 - n[0]);
第三个数 n[2] = rand(0, 100 - n[0] - n[1]);
而且应该也是均匀分布的吧
可以取大于 91 的数。
还是 3 楼的答案最好
感觉三楼解法,稍微有 bug,就是有可能 9 个数全部落在 1 点(9 个点中有重复数出现);个人觉得这是个线性代数问题,先求解线性方程 组 x1+x2+…… x10=100 的所以解,然后从解的集合随机取出。
这不就是微信红包算法吗?
随机十次 每次范围都是 100 减去余下的数
额 说错了 随机九次
就没人想到深度优先搜索嘛,深度优先能用其他图算法就可能能用。
0-100 随机生成 10 个数,然后加起来,如果等于 100,直接输出,不等于就返回继续生成 10 个随机数,直到等于 100 为止,反正现在计算机快,不在乎没效率。
你这是线性方程组求解
先随机十个数字,再求和,得到的结果除以 100 得到 f,对每个数字除以这个值即可。
若需要,还可以高斯分布再 curve 一下
也可以随机出来 10 个,最后再加一个数使 11 个数总和为 100,然后随机出来的每一个数加上 1/10 最后一个数
好吧,有负数…
很简单的问题,你生成 9 个随机数不就是了,最后一个 100- 下。
别杠说你这不是 10 个随机数,你题目本来就做不到 10 个数全部完全随机。
感谢仁兄的思路。
另一个红包算法(精度到 0.01 元)
https://2013.mutoo.im/2015/09/how-wechat-do-the-luckey-money-division.html
计算机是快。。。但这不是写垃圾代码的理由
概率论了解一下,47 楼正解
整数:
穷举所有相加等于 100 的 10 个[0, 100)整数的组合,然后每次随机抽取一组。
非整数:
每次随机完从剩下的份额里面随机就行了。
这个做法,完全就是微信或支付宝的随机红包逻辑。参考 3 楼的方案使用几何角度解决很完美!!
不过实现起来未必容易,要求普通程序员写数学模型算法不见得容易。那么可以先围绕单个平均值随机时候检查大小,每 2 个红包随机结果求和等于 2 个红包总平均值。
楼主啊 看你拿来干嘛了 是要做红包还是只要求随机 红包的话还会多几个约束条件 比如取到小数点后两位 而且虽然随机但差距又不要太大
你这是个伪命题,即使前面九个数据都满足,最后一个数据就不可能是随机数,你这个最终结果是越来越不随机
先生成 10 个随机数 wi,作为权重。然后每个数是 100 * wi / sum(wi)
这个思路也是可以的,10 个数之和大于 100 的,第 11 个数取负数即可,但要保证前 10 个数中最小一个要大于第 11 个数绝对值的 1/10
或者最小的不参与减值,其他的减 1/9,然后又回到刚才的要避免的情况里面了……
又或者是第 11 个数随机分 10 份,哈哈又变成原题了……
感觉顶多只能随机前 9 个,第 10 个妥妥的 100 减去前边的和。。。(其实每次抽,下一个数都是 100 减去前边数的总和吧)
先获得所有的情形:0 ~ 100,共有 101 个数。
假设要求这 10 个数不重复,这 10 个随机数共有:1+2+3+4+5+6+7+8+9+10+11=66 种。(其实不需要统计有多少种)
编写一个循环程序,从 0-100,列举出所有的 10 个不重复的数的组合、与和,循环次数为 101*101。
从这些组合中,让程序自动筛选出所有的和为 100 的(10 个数的)组合。
之后,从这 10 个组合,随机拿出一个组合。
再将这个拿到的组合,再进行随机排列大小。
如果要生成 10 个随机数的和为 200、300,或者 N 个随机数的和为 M,同理。
网上有微信红包算法, 抄一遍就行了,
思路不错
组合数学里的整数拆分了解下,思路:先有一个 1 行 100 列的数组,每个值为 1 ;再随机生成 100 内的 9 个数(升序排列),这样,每个间隔(如 0-随机生成的 9 个数中最小的那个,依次类推会一般有十个间隔)累加起来当做一个值,个人感觉这样很合理,很随机。
学习了。这是数学思维啊。
import random
num_list = []
tmp_num = 0
tmp_sum = 0
get_num = 0
for i in range(1,10):
tmp_sum = i * 10 - get_num
tmp_num = random.randint(1,tmp_sum)
get_num += tmp_num
num_list.append(tmp_num)
num_list.append(100 - get_num)
print(num_list)
先定义一个生成两个数的过程, 并给出总和
第一个数为随机
第二个数为和减去第一个数
返回这两个数
那么 10 个数即为:
先定义一个生成两个数的过程, 参数为其和
第一个数为随机
第二个数为和减去第一个数
返回这两个数
那么 10 个数即为: 求出第一个随机数, 第二个数为第一个数减去第一个数, 用第二个数作为和接着递归
不给代码了(空格按错多回了一次)
比较看脸的算法,供参考(滑稽
from random import randint
sum_ints = 0
while sum_ints != 100:
ints = [randint(0,100) for ii in range(0,10)]
sum_ints = sum(ints)
print(ints)
如果是要均匀分布随机数的话,可以采用 numpy 库里面的狄利克雷分布函数 numpy.random.dirichlet,这个函数能保证随机数是正的,和为 1.
参考 https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.dirichlet.html
import numpy as np
np.random.dirichlet(np.ones(10))*100
那万一那九个数相加已经大于 100 了呢
算法有问题 出现了 0 的时候
我觉得算法还是生成随机数后,用可控总和去减比较稳妥,因为不可能做到完全随机,如果第一个或者有一个随机数是 99 怎么办?我觉得这样比较稳妥:先生成 0~( 100-10 )的随机数 a,然后再生成 0~( a-10 )的随机数,最后一个用综合减去,这样能保证稳妥的随机性
如果第 10 个数用 100 -的话,那有可能这个数是负数吧?
写错了,是 i*11
num1 = [0, 100],num2 = [0, 100 - num1],num3 = [0, 100 - num1 - num2],以此类推,可以吧?
但实际上最可操作的还是取一堆介于 0-1 之间的浮点 /单精 /双精,然后把它们相加,最后总和与 100 等比放大。这个之前#2 操作过了
3 楼正解
照 3 楼说的
import random
a = [random.randint(0, 100) for i in range(10)]
a.append(0)
a.append(100)
a.sort()
b = [a[i + 1] - a[i] for i in range(10 + 1)]
print(b)
print(sum(b))
数字取多了,写成取 11 个数了==
#3 看到这个第一反应:《几何原本》
我能想到的也是这样。
感谢帮助!
我这个思路不知道对不对:
想象你有 100 个小球排成一行,标号 1-100,现在要随机插入 9 块隔板,把它分成 10 段,每段的个数就是你要的结果。
再把隔板视为特殊的小球,问题转化为从 109 个小球中随机选 9 个。
这样的写法其实对于第一位和最后一位是不公平的
你的想法是对的,但是是插入 10 个隔板,因为围成圈后没有开头结尾。
get_sum = -10 初始化的时候,写错了
把 100 进行 10 等分,两两之间随机偏移一下。
这个方法更简明易懂。
你说的办法理论上是可行的,但是实践中是不可用的。
生成的 9 个随机数之和大于 100 的概率太大了,要远远大于正好小于 100 的概率,所以就要反复生成直要碰上符合条件的,极端情况下上秒出不了结果的可能都有,算法时间复杂度不可控制。
还是 3 楼方法科学一些。
那个方法是很垃圾的方法
我试验了一下,连续生成的 9 个 1-100 的随机数之和小于 100 的概率是上百万分之一。
随机取 10 个点,按从小到大排序。取最大的一个放大到 100,其余的根据这个放大比例放大。最后的返回的 10 个随机数就是这 10 个点的间隔,就是 x[10]-x[9],x[9]-x[8]…x[0]
total = 10
count = 10
nTotal = total * 10
from random import randint
for i in range(10):
if nTotal < 1: break
n = randint(1, nTotal)
while n + (10 - i) > nTotal:
n = randint(1, nTotal)
nTotal -= n
print(“person " + str(i) + " got " + str(n/10) + " dollar”)
拿个秒表然后 100 秒内点 9 下暂停 https://www.v2ex.com/static/img/doge.gif
已经给出细节了,见他括号内
你这不是“像”,感觉就“是”啊。
哎哟没发完提交了。
前两天帮朋友解决了这么一个需求,最后也是靠红包算法解决的。
要求给一个数字,如 15.14
再给一个浮动范围,± 0.03
求 5 个数字在浮动范围内,并且平均值==15.14
大小为 10 的数组 选个分布合适的随机函数 循环 100 次 范围 0_99 然后指定下标+1 吧 哈哈哈哈
随机 9 个数,每次 random 的 bound 是(剩余可取的数值+剩余坑数)最后一个减法即可


