怎么能随即生成m个数,让其和等于n?

本文介绍两种生成26个非负随机数的方法,这些数的总和固定为301。第一种方法通过随机切割一根虚拟的绳子来实现;第二种方法则通过301次随机选择数组元素进行累加。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天看到了一个比较有意思的算法题,其实更有意思的是其解法,让人顿时有一种耳目一新的感觉,爱不释手,拿来分享一下。

题目:假设生成26个非负随即数,要求其和是301,求程序生成此列数字

哈哈,元芳,你如何看?

解法一: 关于此种算法原理,我们可以假想是一根长301单位的绳子,然后随即在其上面截25个点,于是得到26根子绳,这26根子绳之和恰好就是绳子总长301。

于是,我们可以:

  1. 初始化27的数组
  2. 该数组0位和26位分别为0和301,其他位置填充0到301之间的随即数字
  3. 对数组排序
  4. 数组相邻数字相减,所得的26个差值即为所求数列。
public static void main(String[] args) {
		int[] arryTemp = new int[27];
		int[] arryRandom = new int[26];
		// get random number,from 0 to 301
		Random ran = new Random((int) Calendar.getInstance().getTimeInMillis());
		arryTemp[0] = 0;
		arryTemp[26] = 301;
		for (int i = 1; i < 26; i++) {
			arryTemp[i] = ran.nextInt(301);
		}

		// sort the arry
		int temp;
		for (int m = arryTemp.length - 1; m > 0; m--) {
			for (int n = 0; n < m; n++) {
				if (arryTemp[m] < arryTemp[n]) {
					temp = arryTemp[n];
					arryTemp[n] = arryTemp[m];
					arryTemp[m] = temp;
				}
			}
		}
		// get the lastest random arry
		for (int j = 0; j < arryRandom.length; j++) {
			arryRandom[j] = arryTemp[j + 1] - arryTemp[j];
		}

		// check the arry
		int sum = 0;
		for (int k = 0; k < arryRandom.length; k++) {
			sum = sum + arryRandom[k];
		}
		System.out.println(sum);
	}

解决方案二,这种方案利用了非负这个不显眼的条件,思路非常简单,就是将1随即加到一个26位数组中,随即301次,有点剑走偏锋,另辟蹊径,让人耳目一新阿,有谋有啊有谋有!
 
	public static void main(String[] args) {
		int[] arryRandom = new int[26];
		Random ran = new Random();
		// add 1 301times into the arry
		for (int i = 0; i < 301; i++) {
			arryRandom[ran.nextInt(26)]++;
		}
		// chenck the arry
		int sum = 0;
		for (int j = 0; j < arryRandom.length; j++) {
			System.out.println(arryRandom[j]);
			sum = sum + arryRandom[j];
		}
		System.out.println(sum);
	}

要调整之前的代码以支持更广泛的随机范围,你需要做两件事情: 1. 修改宏定义`SIZE`的值为你希望的新范围大小; 2. 确保所选范围内所有整都被准确地填充进组中。 具体来说就是改变原有固定为100个元素大小的限制条件至任意指定的最大边界,并相应更新初始化部分。这里给出适用于生成M到N之间非重复随机的一个改进版本例子: ```c #include <stdio.h> #include <stdlib.h> /* 包含rand(), srand() */ #include <time.h> // 定义起始值结束值 #define MIN 5 // 新设定区间的最小值 #define MAX 200 // 新设定区间的最大值 /* 将MIN-MAX之间的字装入列表并打乱它们的位置*/ void initialize_and_shuffle(int* arr, const int min, const int max){ int size = max-min+1; for (int i=0; i<size; ++i){ *(arr+i)=min+i; // 填充从min开始递增直到max的据项 } // 使用Fisher–Yates shuffle算法洗牌 if(size > 1) { for(int i = 0 ; i < size-1 ; ++i ) { int j=i+(rand()/(RAND_MAX/(size-i)+1)); // 避免模运算带来的偏差 // swap values at index 'i' and 'j' int temp=*(*(arr+i)); *(arr+i)=*(arr+j); *(arr+j)=temp; } } } int main(){ int nums[MAX-MIN+1]; // 动态确定组长度 srand((unsigned)time(NULL)); // 根据系统时间设置随机种子 // 这样每次运行都会有不同的结果 initialize_and_shuffle(nums, MIN, MAX); // 初始化及打乱顺序 printf("随机且不重复介于%d与%d之间的整序列为:\n", MIN, MAX ); for(int idx=0;idx<(MAX-MIN+1);++idx) printf("%d ",nums[idx]); putchar('\n'); return EXIT_SUCCESS; } ``` 在这个新的方案里我们加入了两个预处理器指令用于声明用户自定义区间上下限(`#define`);同时新增了一个辅助功能函 `initialize_and_shuffle()` 来完成原组构建以及随即排列的任务。这样做不仅提高了灵活性,也增强了可读性复用率。 另外要注意的是当选择非常大的区间时(例如百万级以上的连续正整集合),考虑内存消耗的问题可能需要采用其他更为高效的方法比如懒加载策略等来进行优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值