若是給你一個問題:「隨機產生和爲S的N個正整數」, 你會如何作呢?java
針對該問題,解決的方法有不少種。在這篇文章中,我將爲你們給出兩種比較好理解的解決方法:一個是「尺子法」;另一個是「鋸木頭法」。 (名字隨便取的,主要是方便理解用)。數組
思想:dom
將給定值S當作一個尺子的長度,那麼,生成N個和爲S的正整數的問題就變成在尺子中尋找出N-1個不一樣的刻度,加上最小刻度0和最大刻度S, 一共有N+1個刻度。而後,從小到大,計算出相鄰刻度的長度,這些長度就能夠認爲是隨機的,由於尺子中產生的N-1個刻度是隨機的。測試
有了上述思想,咱們只要以下三個步驟就能完成這個功能。spa
/*** * <p> * 隨機產生和爲sum(如10)的num(如5)個正整數 * </p> * * @param num * 指望產生的隨機數個數 * @param sum * 全部產生隨機數的和 * @return 返回知足和爲sum的num個隨機正整數組成的數組 */ public Integer[] random(int num, int sum) { /** * Step1: 驗證參數正確性 */ if (num < 1) { throw new IllegalArgumentException("產生隨機數個數的num不能小於1"); } if (sum < num) { throw new IllegalArgumentException("產生隨機數個數的num不能大於和sum"); } if (sum <= 0) { throw new IllegalArgumentException("sum須要爲正整數"); } /** * Step2: 0~sum之間隨機產生num-1個不一樣的刻度 */ Random rand = new Random(); Set<Integer> locations = new TreeSet<>(); while (locations.size() < num - 1) { locations.add(rand.nextInt(sum - 1) + 1); } locations.add(0); locations.add(sum); /** * Step3: 計算出相鄰刻度的差,計算出來的長度就能夠表明一個隨機數 */ Integer[] locationsArray = locations.toArray(new Integer[] {}); Integer[] resultArray = new Integer[num]; for (int i = 0; i < num; i++) { resultArray[i] = locationsArray[i + 1] - locationsArray[i]; } return resultArray; }
思想:code
鋸木頭法的思想則是將S當作木頭的長度,隨機產生和爲S的N個正整數的問題轉換成鋸N-1次木頭,將產生N段小木頭,N段的小木頭其長度和就是S。orm
有了上述思想,咱們即可以經過以下幾個步驟實現該方法:blog
在鋸木頭的時候,須要考慮可鋸的長度。 rem
/*** * <p> * 隨機產生和爲sum(如10)的num(如5)個正整數 * </p> * * @param num * 指望產生的隨機數個數 * @param sum * 全部產生隨機數的和 * @return 返回知足和爲sum的num個隨機正整數組成的數組 */ public int[] random2(int num, int sum) { /** * Step1: 驗證參數正確性 */ if (num < 1) { throw new IllegalArgumentException("產生隨機數個數的num不能小於1"); } if (sum < num) { throw new IllegalArgumentException("產生隨機數個數的num不能大於和sum"); } if (sum <= 0) { throw new IllegalArgumentException("sum須要爲正整數"); } /** * 若是隻有一個 直接返回 */ if (num == 1) { return new int[] { sum }; } /** * Step2: 鋸木頭的方法 */ Random rand = new Random(); int[] randomNumbers = new int[num]; // 剩餘數 int remainingNum = sum; for (int i = 0; i < num - 1; i++) { /** * 能夠鋸掉可選值 * remaining - (num - (i+1)) + 1 = remainingNum - num + i + 1 */ int randNum = rand.nextInt(remainingNum - num + i + 1) + 1; remainingNum -= randNum; randomNumbers[i] = randNum; } /** * 最後一個直接賦值便可 */ randomNumbers[num - 1] = remainingNum; return randomNumbers; }
詳細代碼及某次測試運行結果以下:io
import java.util.Random; import java.util.Set; import java.util.TreeSet; /** * @author wangmengjun * */ public class RandomExample_1 { /*** * <p> * 隨機產生和爲sum(如10)的num(如5)個正整數 * </p> * * @param num * 指望產生的隨機數個數 * @param sum * 全部產生隨機數的和 * @return 返回知足和爲sum的num個隨機正整數組成的數組 */ public Integer[] random(int num, int sum) { /** * Step1: 驗證參數正確性 */ if (num < 1) { throw new IllegalArgumentException("產生隨機數個數的num不能小於1"); } if (sum < num) { throw new IllegalArgumentException("產生隨機數個數的num不能大於和sum"); } if (sum <= 0) { throw new IllegalArgumentException("sum須要爲正整數"); } /** * Step2: 0~sum之間隨機產生num-1個不一樣的刻度 */ Random rand = new Random(); Set<Integer> locations = new TreeSet<>(); while (locations.size() < num - 1) { locations.add(rand.nextInt(sum - 1) + 1); } locations.add(0); locations.add(sum); /** * Step3: 計算出相鄰刻度的差,計算出來的長度就能夠表明一個隨機數 */ Integer[] locationsArray = locations.toArray(new Integer[] {}); Integer[] resultArray = new Integer[num]; for (int i = 0; i < num; i++) { resultArray[i] = locationsArray[i + 1] - locationsArray[i]; } return resultArray; } /*** * <p> * 隨機產生和爲sum(如10)的num(如5)個正整數 * </p> * * @param num * 指望產生的隨機數個數 * @param sum * 全部產生隨機數的和 * @return 返回知足和爲sum的num個隨機正整數組成的數組 */ public int[] random2(int num, int sum) { /** * Step1: 驗證參數正確性 */ if (num < 1) { throw new IllegalArgumentException("產生隨機數個數的num不能小於1"); } if (sum < num) { throw new IllegalArgumentException("產生隨機數個數的num不能大於和sum"); } if (sum <= 0) { throw new IllegalArgumentException("sum須要爲正整數"); } /** * 若是隻有一個 直接返回 */ if (num == 1) { return new int[] { sum }; } /** * Step2: 鋸木頭的方法 */ Random rand = new Random(); int[] randomNumbers = new int[num]; // 剩餘數 int remainingNum = sum; for (int i = 0; i < num - 1; i++) { /** * 能夠鋸掉可選值 * remaining - (num - (i+1)) + 1 = remainingNum - num + i + 1 */ int randNum = rand.nextInt(remainingNum - num + i + 1) + 1; remainingNum -= randNum; randomNumbers[i] = randNum; } /** * 最後一個直接賦值便可 */ randomNumbers[num - 1] = remainingNum; return randomNumbers; } }
import java.util.Arrays; /** * @author wangmengjun * */ public class Main { public static void main(String[] args) { RandomExample_1 example1 = new RandomExample_1(); int num = 6; int sum = 30; System.out.println(String.format("隨機產生和爲%d的%d個正整數", sum, num)); for (int i = 1; i <= 10; i++) { System.out.println(String.format("第%d遍random()產生結果 -- %s", i, Arrays.toString(example1.random(num, sum)))); System.out.println(String.format("第%d遍random2()產生結果 -- %s", i, Arrays.toString(example1.random2(num, sum)))); } } }
隨機產生和爲30的6個正整數
第1遍random()產生結果 -- [2, 4, 4, 6, 5, 9]
第1遍random2()產生結果 -- [24, 1, 2, 1, 1, 1]
第2遍random()產生結果 -- [6, 4, 1, 1, 6, 12]
第2遍random2()產生結果 -- [17, 1, 5, 5, 1, 1]
第3遍random()產生結果 -- [1, 15, 1, 6, 3, 4]
第3遍random2()產生結果 -- [2, 4, 1, 7, 9, 7]
第4遍random()產生結果 -- [16, 1, 1, 4, 5, 3]
第4遍random2()產生結果 -- [11, 4, 6, 5, 1, 3]
第5遍random()產生結果 -- [4, 4, 6, 7, 4, 5]
第5遍random2()產生結果 -- [6, 13, 1, 3, 6, 1]
第6遍random()產生結果 -- [10, 1, 16, 1, 1, 1]
第6遍random2()產生結果 -- [18, 7, 2, 1, 1, 1]
第7遍random()產生結果 -- [4, 1, 10, 8, 2, 5]
第7遍random2()產生結果 -- [8, 6, 6, 4, 3, 3]
第8遍random()產生結果 -- [1, 6, 3, 8, 1, 11]
第8遍random2()產生結果 -- [4, 7, 3, 7, 2, 7]
第9遍random()產生結果 -- [3, 5, 13, 3, 1, 5]
第9遍random2()產生結果 -- [13, 4, 1, 4, 2, 6]
第10遍random()產生結果 -- [4, 5, 12, 3, 3, 3]
第10遍random2()產生結果 -- [17, 3, 7, 1, 1, 1]