Python中如何生成10个和为100的[0,100]随机数?

感谢大家的帮助,看了大家的回复后感觉确实有点像微信红包的算法,但是红包是不包含 0 的。另外,我也感觉标题中的“随机”这个表述有问题,应该是 [从 [0,100] 取 10 个数,使之和等于 100,可取 0 和 100 ] 不知道这次表述是否合适了。
Python中如何生成10个和为100的[0,100]随机数?

101 回复

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. 在1-99之间随机选择9个分割点
  2. 计算相邻分割点(包括0和100)的差值得到10个随机数
  3. 通过while循环确保所有数都在[0,100]范围内

算法特点:

  • 每个数都是均匀随机生成的
  • 保证总和严格等于100
  • 每个数都在[0,100]区间内
  • 时间复杂度O(1),因为最多几次重试就能得到有效结果

一句话建议:用分割区间法生成随机数既简单又保证分布均匀。

这就不叫随机数吧…

而且最简单的方法就是随机生成,然后总和 /100 = k,每个数再 /k

在一根 1 到 100 的数轴上,随机取 9 个点,拿到 10 个线段。计算每个线段的长度,即是取值。

牛逼

需要补全一下问题,比如你现在包含 0,是否确定?另外 10 个随机数可以重复吗?
如果不可以重复,那第一步取值范围就剩 1-55 或者 0-64 了。

#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]

fisher yates 洗牌算法,生成 1~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 个数全部完全随机。

感谢仁兄的思路。

计算机是快。。。但这不是写垃圾代码的理由

概率论了解一下,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 减去前边数的总和吧)

坐等比 3# 更牛 B 的方法

先获得所有的情形: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 个数中最小的那个,依次类推会一般有十个间隔)累加起来当做一个值,个人感觉这样很合理,很随机。

其实题主的问题有两个分支:
1. 直接获得 10 个数字
2. 每次生成一个,一共生成 10 个数字

学习了。这是数学思维啊。

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 个。

这样的写法其实对于第一位和最后一位是不公平的

看错了,是 0-100。
那就是从 0-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 是(剩余可取的数值+剩余坑数)最后一个减法即可

回到顶部