简体   繁体   English

生成 n 个随机数,其总和为 m 且所有数应大于零

[英]Generate n random numbers whose sum is m and all numbers should be greater than zero

I want to generate 9 non zero random numbers whose sum is 250. I have tried following code it gives me 9 random numbers but some numbers are zero.我想生成 9 个非零随机数,其总和为 250。我尝试了以下代码,它给了我 9 个随机数,但有些数字为零。

 public void n_random()
{
  Random r = new Random();
ArrayList<Integer> load = new ArrayList<Integer>();
    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);

}

Where is my mistake or where i should improve my code or any other solution?我的错误在哪里或者我应该在哪里改进我的代码或任何其他解决方案?

I would suggest using:我建议使用:

temp = r.nextInt((250 - sum) / (9 - i)) + 1;

That will make sure that:这将确保:

  • each number is strictly positive每个数字都是严格的正数
  • you won't use the full "250 allowance" before reaching the 9th number在达到第 9 个数字之前,您不会使用完整的“250 津贴”

However the distribution of the results is probably biased.然而,结果的分布可能是有偏差的。

Example output:示例输出:

Random arraylist [18, 28, 22, 19, 3, 53, 37, 49, 21]随机数组列表 [18, 28, 22, 19, 3, 53, 37, 49, 21]

Explanation:解释:

  • (250 - sum) is the amount left to reach 250, so you don't want to go over that (250 - sum)是达到 250 的剩余金额,所以你不想超过那个
  • / (9 - i) if your sum has reached for example 200 (need 50 more) and you have 5 more to go, make sure the next random number is not more than 10, to leave some room for the next 4 draws / (9 - i)如果您的总和已达到例如 200(需要 50 多个)并且您还有 5 个可以使用,请确保下一个随机数不超过 10,以便为接下来的 4 次抽奖留出一些空间
  • + 1 to prevent 0 + 1防止 0

An alternative which probably gives a better distribution is to take random numbers and scale them to get to the desired sum.另一种可能提供更好分布的替代方法是采用随机数并将它们缩放以获得所需的总和。 Example implementation:示例实现:

public static void n_random(int targetSum, int numberOfDraws) {
    Random r = new Random();
    List<Integer> 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));
}

Generate n random numbers whose sum is m and all numbers should be greater than zero生成 n 个随机数,其总和为 m 且所有数应大于零

The following is basically what you were trying to achieve.以下基本上是您想要实现的目标。 Here, it's written in Perl, since I don't know Java well, but it should be easy to translate.在这里,它是用 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.

The problem with your approach is that you'll end up with a lot of small numbers.你的方法的问题是你最终会得到很多小数字。 Five sample runs with the numbers in ascending order:五个样本按升序运行:

  • 1, 1, 1, 1, 2, 3, 6, 50, 185 1, 1, 1, 1, 2, 3, 6, 50, 185
  • 1, 1, 1, 1, 2, 3, 4, 13, 224 1, 1, 1, 1, 2, 3, 4, 13, 224
  • 1, 1, 1, 1, 1, 3, 8, 11, 223 1, 1, 1, 1, 1, 3, 8, 11, 223
  • 1, 1, 1, 1, 2, 4, 19, 103, 118 1, 1, 1, 1, 2, 4, 19, 103, 118
  • 2, 2, 9, 11, 11, 19, 19, 68, 109 2, 2, 9, 11, 11, 19, 19, 68, 109

A better approach might be to take N random numbers, then scale them so their sum reaches M. Implementation:更好的方法可能是取 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);

Five sample runs:五个样本运行:

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

Your code line:您的代码行:

r.nextInt(250 - sum);

... will generate a pseudo-random from 0 ( included ) to 250 - sum ( excluded ). ... 将生成从0包括)到250 - sum排除)的伪随机250 - sum

See API for Random.nextInt .请参阅Random.nextInt API

I won't try to solve all your problem here, but simply adding 1 to the expression above would guarantee that it never returns 0 .我不会在这里尝试解决您的所有问题,但只需在上面的表达式中添加1就可以保证它永远不会返回0

All remaining adaptations up to you though :)不过,所有剩余的调整都取决于您:)

For instance if 250 - sum - 1 evaluates to negative, then you'll throw an IllegalArgumentException .例如,如果250 - sum - 1计算结果为负,那么您将抛出IllegalArgumentException

I don't see where you've checked to make sure that zero is excluded.我没有看到您检查过以确保排除零的位置。 Add a check before you insert it into the array.在将其插入数组之前添加检查。

Too many "magic numbers" in this code to suit me.这段代码中有太多的“神奇数字”不适合我。

A call to Random.nextInt(n) will return an integer between 0 and n-1Random.nextInt(n)调用将返回 0 到 n-1 之间的整数

try temp = r.nextInt(250 - sum) + 1;尝试temp = r.nextInt(250 - sum) + 1; and see if that solves your issue.看看这是否能解决您的问题。

Here's one way to do it, that avoids (most) magic numbers and provides a decent distribution of numbers though all will be smaller than other possible solutions.这是一种方法,可以避免(大多数)幻数并提供适当的数字分布,尽管所有数字都比其他可能的解决方案小。

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);
}

this is javascript alternative这是 javascript 替代品

function getRandomNos(m,n) {

    var nums=[];
    var i;

    for (i = 0;i<n;i++) {
       nums[i] = Math.random();
    }
    var factor = (m-n) / nums.reduce(add, 0);
    for (i = 0;i<nums.length;i++) {
       nums[i] = parseInt(nums[i] * factor) + 1;
    }

    var fudge = m - nums.reduce(add, 0);

    for (i=0;i<fudge;i++) {
       nums[i] = nums[i] + 1;
    }
    console.log(nums);
    console.log(nums.reduce(add, 0));

}

function add(a, b) {
    return a + b;
}
//Perl code translated to Java
//Its working and no 0's !!!!

import java.util.*;
import java.util.stream.*;enter code here

public class MyClass {
    public static void main(String args[]) {
     int numberOfDraws = 17;
     int targetSum = 40;
     


Random r = new Random();


List<Integer> load = new ArrayList<>();



int sum = 0;
for (int i = 0; i < numberOfDraws; i++) {
        int next = r.nextInt(targetSum) + 1;
        load.add(next);
        sum += next;
        System.out.println("Arraylist first loop " + load.get(i));
    }
    
    
double factor = (((double)targetSum)-((double)numberOfDraws)) / ((double)sum);
System.out.println("Factor value: " + factor);

int newSum =0;
for (int i = 0; i < numberOfDraws; i++) {
    load.set(i, (int) ((load.get(i) * factor)) + 1);
    newSum += load.get(i);
    
    System.out.println("Arraylist second loop " + load.get(i));
}



int fudge = targetSum - newSum;
for (int i = 0; i < fudge; i++) {

   
   int y = r.nextInt(numberOfDraws);
        load.set(i, load.get(i) + 1);
   
}

System.out.println("Random arraylist " + load);

    }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM