`
wangmengjun
  • 浏览: 6062 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

随机产生和为S的N个正整数

阅读更多

如果给你一个问题:“随机产生和为S的N个正整数”, 你会如何做呢?

 

针对该问题,解决的方法有很多种。在这篇文章中,我将为大家给出两种比较好理解的解决方法:一个是“尺子法”;另外一个是“锯木头法”。 (名字随便取的,主要是方便理解用)。

 

方法一:尺子法

 

思想:将给定值S看成一个尺子的长度,那么,生成N个和为S的正整数的问题就变成在尺子中寻找出N-1个不同的刻度加上最小刻度0和最大刻度S 一共有N+1个刻度然后,从小到大,计算出相邻刻度的长度,这些长度就可以认为是随机的,因为尺子中产生的N-1个刻度是随机的。

有了上述思想,我们只要如下三个步骤就能完成这个功能。

 

  1. 验证参数S和N的正确性
  2. 尺子中产生N-1个不同刻度
  3. 计算相邻刻度之间的值

 

/***
	 * <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;
	}

 

 

 方法二:锯木头法

 

思想:锯木头法的思想则是将S看成木头的长度,随机产生和为S的N个正整数的问题转换成锯N-1次木头,将产生N段小木头,N段的小木头其长度和就是S

 


 

有了上述思想,我们便可以通过如下几个步骤实现该方法:

 

  1. 验证参数S和N的正确性
  2. 锯N-1次木头

在锯木头的时候,需要考虑可锯的长度大笑 

 

	/***
	 * <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.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]

 

  • 大小: 45.1 KB
  • 大小: 1 MB
分享到:
评论

相关推荐

    产生1到N的不重复随机数

    这个函数接受两个参数,分别是随机数的最小值(包含)和最大值(包含),并返回该范围内的一个随机整数。示例代码如下: ```python import random def generate_random(N): return random.randint(1, N) ``` ...

    随机数个相互独立的随机变量之和的分布函数

    随机变量N是取正整数值的独立随机变量,它的概率质量函数为p_N(n)。在这种情况下,随机变量Y的矩母函数M_Y(s)可以通过以下步骤计算: 1. 基于N=n时,随机变量Y的矩母函数是E[e^{s(X1+...+X_n)}|N=n],这等于每个Xi...

    世界500强面试题.pdf

    1.5.6. 输入两个整数 n 和 m,从数列 1,2,3.......n 中 随意取几个数 ....... 116 1.5.7. 输入一个表示整数的字符串,把该字符串转换成整数并输出.............. 118 1.5.8. 给出一个数列,找出其中最长的单调...

    大一python基础编程题-基本编程题-python.pdf

    4. 反转输出正整数 - `input()`函数用于接收用户输入,`eval()`函数则可以将字符串转换为对应的数值类型。`s[::-1]`表示字符串的反向切片,可以实现字符串的反转。`print(eval(s[::-1]))`将反转后的字符串转换为...

    c语言各种排序

    快速排序的平均时间复杂度为O(n log n),最坏情况为O(n^2)。 #### 四、简单选择排序(堆排序) **定义:** 简单选择排序在这里实际上指的是堆排序。堆排序是一种基于比较的排序技术,它使用二叉堆(通常是最大堆)...

    实验1 控制语句上机.pdf

    - 生成N个100以内的随机正整数并存入数组。 2. **显示数组**: - 输出数组s[]的初始数据。 3. **查找最大值与最小值**: - 遍历数组,找到最大值和最小值。 4. **排序数组**: - 对数组s[]进行降序排序。 5. **复制...

    随机过程第四章马氏链课件

    第二个定义更为直观,它说明了马氏链是一个随机序列{Xn,n=0,1,2,...},其状态空间为S,对于任意正整数n和任意的状态序列{i0,i1,...,in},序列{Xn,n=0,1,2,...}的状态序列满足马氏链性质。 在随机过程中,马氏链的...

    明明的随机数

    第2行有N个用空格隔开的正整数,为所产生的随机数。 输出格式 输出也是2行,第1行为1个正整数M,表示不相同的随机数的个数。第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。 p.s.c#实现,输出一...

    上海电机学院C语言实训答案

    输入一个正整数n (1&lt;n),再输入n 个整数,将最小值与第一个数交换,最大值与最后一个数交换,然后输出交换后的n 个数。 (25)抓住肇事者 一辆卡车违反交通规则,撞人后逃跑。现场共有三个目击者,但都没有记住车号...

    伪随机数生成算法及比较

    这里 \(λ, c, M\) 以及初始值 \(x_0\) 均为正整数。若 \(c ≠ 0\) 且 \(λ ≠ 1\),则称为混合同余法。 - **乘同余法**:当 \(c = 0\) 时,递推公式变为 \(x_{n+1} = λx_n \mod M\)。乘同余法在实际应用中表现较...

    随机数怎么产生的

    - \( m \) 是模数(通常是正整数)。 这个公式的关键在于选择合适的参数 \( a \), \( c \), 和 \( m \),使得生成的随机数序列具有良好的随机性和周期性。 #### 参数的选择 - **模数 \( m \)**:通常选择为 \( 2^...

    matlab开发-Pellm

    Pell方程的解通常由无穷序列构成,而"Pellm"函数似乎提供了一种获取这个序列中前n个正整数解的方法。 "Pell.m"是这个功能的核心代码,很可能包含了递归或迭代算法来生成这些解。在MATLAB编程中,这样的实现可能涉及...

    Random Graphs and Complex Networks随机图与复杂网络

    Ramsey数\( R(k) \)定义为最小的整数\( n \),使得对于任意的图\( G \)(如果图\( G \)的大小至少为\( n \)),要么\( G \)自身,要么其补图包含一个大小至少为\( k \)的完全子图。Erdős证明了\( R(k) \)至少为\( 2...

    数据结构例程

    阶乘是数学中的一个概念,表示所有小于等于给定正整数n的所有正整数的乘积。例程`factorsum`使用两个嵌套循环计算n的阶乘之和。外层循环遍历1到n,内层循环计算每个i的阶乘并累加到总和f。 2. **顺序表的插入操作*...

    拉姆齐定理(维基百科).rar

    在最简单的形式下,拉姆齐定理陈述了这样一个事实:对于任意给定的正整数r和s,存在一个最小的正整数R(r,s),使得当任何n个点被染成两种颜色时,至少会形成一个大小为r的同色完全子集或者一个大小为s的同色完全子集...

    VB编程题及答案.docx

    - **解析**:随机生成10个1~100的正整数,使用循环结构计算这些数的最大值、最小值和平均值。 #### 35. 将元素插入已排序的一维数组 - **知识点**:数组操作,排序 - **解析**:遍历数组a(),找到合适的插入位置,...

    新的周期为pm的GF(h)上广义割圆序列的线性复杂度

    在线性复杂度的定义中,如果有一个周期为N的序列s,线性复杂度是指最小的正整数l,使得存在一个次数不超过l-1的非零多项式g(x),使得序列s可以通过g(x)的线性组合来表示,即g(x)乘以序列s等于零序列。从线性反馈移位...

    大学Python 第三章 习题答案.docx

    该习题要求学生编写一个Python程序,输入一个正整数n,求n以内能被17整除的最大正整数。 习题7:无穷级数 该习题要求学生编写一个Python程序,计算S=1+1/3-1/5+1/7-1/9……的结果。程序需要输入项数n,然后使用循环...

    大学Python 第三章 习题答案.pdf

    通过循环语句遍历输入的正整数 n,找到 n 以内能被 17 整除的最大正整数。 7. 数列计算 知识点:循环语句、数学函数 本题目考察了循环语句的使用和数学函数的应用。通过循环语句遍历输入的项数 n,计算 S=1+1/3-1...

    素数的判断

    "素数的判断"这个主题主要涉及如何通过编程来确定一个给定的正整数是否为素数。在描述中提到了"用MIller-Rabin素判定法则",这是一种高效的概率性测试方法,用于判断一个大整数是否为素数。该算法由Melvin Rabin和...

Global site tag (gtag.js) - Google Analytics