生成n个随机数,其总和为m,所有数字应大于零
我想生成9个非零随机数,其总和为250.我试过下面的代码,它给了我9个随机数,但有些数字为零。
public void n_random() { Random r = new Random(); ArrayList load = new ArrayList(); int temp = 0; int sum = 0; for (int i = 1; i <= 9; i++) { if (!(i == 9)) { temp = r.nextInt(250 - sum); System.out.println("Temp " + (i) + " " + temp); load.add(temp); sum += temp; } else { int last = (250 - sum); load.add(last); sum += last; } } System.out.println("Random arraylist " + load); System.out.println("Sum is "+ sum); }
我的错误在哪里或我应该在哪里改进我的代码或任何其他解决方案?
我建议使用:
temp = r.nextInt((250 - sum) / (9 - i)) + 1;
这将确保:
- 每个数字都是正数
- 在达到第9个数字之前,你不会使用完整的“250补贴”
然而,结果的分布可能有偏差。
输出示例:
随机arraylist [18,28,22,19,3,53,37,49,21]
说明:
-
(250 - sum)
是剩下达到250的金额,所以你不想过去 -
/ (9 - i)
如果您的金额已达到例如200(需要50多个)并且您还有5个要去,请确保下一个随机数不超过10,为接下来的4个绘制留出一些空间 -
+ 1
以防止0
可能提供更好分布的替代方案是采用随机数并对它们进行缩放以得到所需的总和。 示例实现:
public static void n_random(int targetSum, int numberOfDraws) { Random r = new Random(); List load = new ArrayList<>(); //random numbers int sum = 0; for (int i = 0; i < numberOfDraws; i++) { int next = r.nextInt(targetSum) + 1; load.add(next); sum += next; } //scale to the desired target sum double scale = 1d * targetSum / sum; sum = 0; for (int i = 0; i < numberOfDraws; i++) { load.set(i, (int) (load.get(i) * scale)); sum += load.get(i); } //take rounding issues into account while(sum++ < targetSum) { int i = r.nextInt(numberOfDraws); load.set(i, load.get(i) + 1); } System.out.println("Random arraylist " + load); System.out.println("Sum is "+ (sum - 1)); }
生成n个随机数,其总和为m,所有数字应大于零
以下基本上是您要实现的目标。 这里,它是用Perl编写的,因为我不太熟悉Java,但它应该很容易翻译。
use strict; use warnings; use feature qw( say ); use List::Util qw( shuffle ); my $m = 250; my $n = 9; my @nums; while ($n--) { my $x = int(rand($m-$n))+1; # Gen int in 1..($m-$n) inclusive. push @nums, $x; $m -= $x; } say join ', ', shuffle @nums; # shuffle reorders if that matters.
你的方法的问题是你会得到很多小数字。 五个样本运行,数字按升序排列:
- 1,1,1,1,2,3,6,50,185
- 1,1,1,1,2,3,4,13,224
- 1,1,1,1,1,3,8,11,223
- 1,1,1,1,2,4,19,103,118
- 2,2,9,11,11,19,19,68,109
更好的方法可能是采用N个随机数,然后对它们进行缩放,使其总和达到M.实施:
use strict; use warnings; use feature qw( say ); use List::Util qw( sum ); my $m = 250; my $n = 9; # Generate $n numbers between 0 (incl) and 1 (excl). my @nums; for (1..$n) { push @nums, rand(); } # We subtract $n because we'll be adding one to each number later. my $factor = ($m-$n) / sum(@nums); for my $i (0..$#nums) { $nums[$i] = int($nums[$i] * $factor) + 1; } # Handle loss of fractional component. my $fudge = $m - sum(@nums); for (1..$fudge) { # Adds one to a random number. ++$nums[rand(@nums)]; } say join('+', @nums), '=', sum(@nums);
五个样本运行:
32+32+23+42+29+32+29+20+11=250 31+18+25+16+11+41+37+56+15=250 21+15+40+46+22+40+32+1+33=250 34+24+18+29+45+30+19+29+22=250 3+45+20+6+3+25+18+65+65=250
你的代码行:
r.nextInt(250 - sum);
…将生成从0
( 包括 )到250 - sum
( 排除 )的伪随机。
请参阅Random.nextInt
API 。
我不会尝试在这里解决你的所有问题,但只需在上面的表达式中加1
就可以保证它永远不会返回0
。
尽管如此,所有剩余的改编都取决于你:)
例如,如果250 - sum - 1
计算结果为负数,那么您将抛出IllegalArgumentException
。
我没有看到你检查的位置,以确保排除零。 在将其插入数组之前添加一个检查。
此代码中有太多“神奇数字”以适合我。
对Random.nextInt(n)
调用将返回0到n-1之间的整数
试试temp = r.nextInt(250 - sum) + 1;
看看是否能解决你的问题。
这是一种方法,它可以避免(大多数)魔术数字并提供合适的数字分布,尽管所有数字都会比其他可能的解决方案更小。
public static void n_random(int count, int finalSum) { Random r = new Random(); int numbers[] = new int[count]; int sum = 0; for (int i = 0; i < count - 1; i++) { numbers[i] = r.nextInt((finalSum - sum) / 2) + 1; sum += numbers[i]; } numbers[count - 1] = finalSum - sum; StringBuilder numbersString = new StringBuilder("Random number list: "); for (int i = 0; i < count; i++) numbersString.append(numbers[i] + " "); System.out.println(numbersString); } public static void main(String[] args) { n_random(9, 250); }
这是javascript替代品
function getRandomNos(m,n) { var nums=[]; var i; for (i = 0;i