(已解决) 随机生成 n 个指定范围内的数使其和为目标值
大佬们,我想求助一个关于随机数的写 (算) 法问题,随机生成 n 个指定范围内的数使其和为目标值。
平时工作中会碰到一些凑数的问题,比如开发票时,希望能挑出若干个数使其和等于或者接近于某个值,又或者根据一个总数拆分成某个区间内的 n 个数,各种折腾。
关于第一种组合凑数,之前也求助过类似的问题: 排列组合两种递归写法的效率比较
主要是想求助一下第二种总数随机拆分的情景,我自己折腾了几种不靠谱的写法:
1、随机生成 n 个非负整数使其和为某个值,比如随机生成 3 个整数使其和为 723
=Cnt=3,Sum=723,((Cnt-1).(rand(Sum)).sort()|Sum).((~-~[-1]))
这种写法比较简单,缺点是数值的随机分布不均匀,有时候小的很小,大的很大,也不能指定数字生成的范围。
2、于是有了第 2 种写法,用 iterate 迭代无数次,当满足条件时跳出迭代。比如以下写法,随机生成 5 个在区间 [10,20] 内的不重复的整数,使其和等于 72。这种写法就比较傻了,虽然不烧人脑,但烧电脑,出结果的速度完全靠运气,运气不好时,迭代次数跑完也没有正确结果出来,如果设定一个 infinite 的次数,那电脑会跑崩溃。
=spl("=Min=10,Max=20,Cnt=5,Sum=72 /round(rand()*(Max-Min),2),rand(Max-Min+1)
=9e7.iterate(Cnt.(Min+rand(Max-Min)),,~~.sum()==Sum&&(~~.icount()==Cnt))")
3、第 3 种写法是先生成 n-1 个指定区间的随机数,然后用目标值去减去这 n-1 个随机数的和,得到的差作为最后一个数,但这个数要根据大小看是否落在指定区间里进行修正。如果数字允许重复那还好,如果要求不重复,我就不道怎么玩下去了。比如以下语句:
可复制代码如下:
A | B | C | D | |
1 | >Min=10,Max=1000 | / 指定数值范围 | ||
2 | >N=4,Tgt=2024 | / 生成 N 个数,和等于 Tgt | ||
3 | =(N-1).(Min+rand(Max-Min+1)) | =A3.m(:) | / 先生成 N-1 个指定范围内的随机数 | |
4 | =Tgt-A3.sum() | =A4 | / 最后一个数用 Tgt 减去上述 N-1 个数的和得到 | |
5 | / 判断最后一个数 A4 是否符合范围,根据大小进行修正。B3、B4 没什么用,只是为了观察初始值 | |||
6 | for | if A4>=Max | =d=rand(A4-Max+1)+1 | / 随机生成一个数 |
7 | =i=rand(N-1)+1 | / 在 N-1 个位置中生成一个随机位置 | ||
8 | =v=A3(i)+d | / 在随机位加上随机数,判断是否合适 | ||
9 | if between(v,Min:Max) | >A3(i)=v | ||
10 | >A4-=d | |||
11 | else | next | ||
12 | else if A4<=Min | =d=rand(Min-A4+1)+1 | ||
13 | =i=rand(N-1)+1 | |||
14 | =v=A3(i)-d | |||
15 | if between(v,Min:Max) | >A3(i)=v | ||
16 | >A4+=d | |||
17 | else | next | ||
18 | else | break | ||
19 | =A3|A4 | |||
20 | =A19.sum()==Tgt&&(A19.cand(between(~,Min:Max))) |
以上 3 种写法我觉得不咋靠谱:
第一种有点草率,不能指定数字分布的范围,且不能去重;
第二种烧电脑,快慢看运气;
第三种不能随机不重复取值。
这个问题对我来说一直是个困扰,所以,我想恳请大佬们帮忙看看,有闲有心情的时候可否指导一下🙏 🙏 比如,随机数能正态分布 (比较均匀分布在某个区间,不知道表达是否正确),随机数允许重复,随机数不许重复,随机数可以是小数,随机数只能是正整数…等等情况,怎么感觉情况有点大,越说越没边了😄
谢谢🙏 🙏
x=n.(s\n + if(~<=x%n,1,0) )
s.run(a=rand(n)+1, b=rand(n)+1, if(x(a)<ma && x(b)>mi, (x(a)+=1,x(b)-=1)))
😂 谢谢大神出手🙏
我再多问一句,这个写法如果要实现 n 个不重复元素 (在可以满足不重复的情况下),要如何添加条件?
如果数量不是很大可以叉乘再过滤,=xjoin(${5.(“to(10,20)”).concat(“;”)}).(~.array()).select(…)
谢谢大佬指导🙏
xjoin 确实很强,我笔记里的神仙函数之一,特别是参数里写条件过滤,屌的很😂 ,以前的笔记:
唯一有点不方便的地方就是,如果取 n 个数时这个 n 是 10 个以上时,写条件就会比较多。
我有个问题: 叉乘时在参数里写过滤条件跟叉乘完了之后再用 select 筛选出来,这两个写法的时间复杂度一样吗?我主观认为在参数里写过滤条件就相当于是循环时剪枝,会减少点复杂度。😂
复杂度是一样的,过滤条件写在 xjoin 里会减少记录的产生,理论上会快一点
懂了,谢谢大佬🙏 🙏