[当算法遇上数学]元芳,你怎么能随即生成m个数,让其和等于n?(加强版)
2012-10-31 15:24 立雪三尺 阅读(3097) 评论(35) 编辑 收藏 举报今天看到了一个比较有意思的算法题,其实更有意思的是其解法,让人顿时有一种耳目一新的感觉,爱不释手,拿来分享一下。
题目:假设生成26个非负随即数,要求其和是301,求程序生成此列数字
哈哈,元芳,你如何看?
解法一: 关于此种算法原理,我们可以假想是一根长301单位的绳子,然后随即在其上面截25个点,于是得到26根子绳,这26根子绳之和恰好就是绳子总长301。
于是,我们可以:
- 初始化27的数组
- 该数组0位和26位分别为0和301,其他位置填充0到301之间的随即数字
- 对数组排序
- 数组相邻数字相减,所得的26个差值即为所求数列。
class Random301 { 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 )DateTime.Now.Ticks); arryTemp[0] = 0; arryTemp[26] = 301; for ( int i = 1; i < 26; i++) { arryTemp[i] = ran.Next(0,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]; } Console.WriteLine(sum); Console.ReadKey(); } } |
解决方案二,这种方案利用了非负这个不显眼的条件,思路非常简单,就是将1随即加到一个26位数组中,随即301次,有点剑走偏锋,另辟蹊径,让人耳目一新阿,有谋有啊有谋有!
class Radom301Arry { 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.Next(0, 26)]++; } //chenck the arry int sum = 0; for ( int j = 0; j < arryRandom.Length; j++) { Console.WriteLine(arryRandom[j]); sum = sum + arryRandom[j]; } Console.WriteLine( "sum:" +sum); Console.ReadKey(); } } |
多谢@另一个石头,@八字和尚,@firstrose等等朋友的质疑指证,我测试了一下,如果将数字增大,i <3000001,确实得出来的数字比较的平均,看来我扔了我这块石头确实引来了不少玉啊,嘿嘿,我仔细的看了下,第二种算法确实隐藏着缺陷,此方法的意图是通过利用概率的随机性,等于将301个1按照等概率分布在26个位置,random函数均匀抛出数字,所以其分布应该大概按照301/26分布,在301次的时候其实已经有表现了,当数字变大此种现象更加明显。
不过由于此种算法确实太过奇妙,所以我觉得用于小数字的随机还是可以的,元芳,你怎么看呢?
第三种解法:下象棋的朋友都知道一种常见的开局方式,兵三进一或者兵七进一,即仙人指路局,此种着法能进能退,能起马能飞炮,算起来中规中矩,其实也不乏一种方法,于是我也中规中矩的按照正常的思路又写了一种所谓的“中规中矩”的算法:
class List301 { static void Main( string [] args) { //store the 26 random numbers List< int > templist = new List< int >(); Random ran = new Random(( int )DateTime.Now.Ticks); int temp = 0; int total = 301; for ( int i = 0; i < 26; i++) { if (25 != i) temp = ran.Next(0, total); else temp = total; total = total - temp; templist.Add(temp); } int sum = 0; for ( int m = 0; m <templist.Count; m++) { sum = sum + templist[m]; Console.WriteLine(m+ ":" +templist[m]); } Console.WriteLine(sum); Console.ReadKey(); } } |
这种方法就是先从0-301之间random出来一个数字,然后301减去此数字得出目前需要抛出数字的总和,然后再从0-目前总合中再random出来一个数字。。。如此知道第26个数字,不再random,直接赋值为最后剩下的目前数字之和,测试后发现这个方法最后会抛出大串的0,也在意料之中,因为随着random次数的增加,random的震荡范围越来越小,最终会在大于0附近徘徊。
鉴于此种现象,稍微改进了一下方案,控制了一下random的震荡范围:
class List301 { static void Main( string [] args) { //store the 26 random numbers List< int > templist = new List< int >(); Random ran = new Random(( int )DateTime.Now.Ticks); int temp = 0; int total = 301; for ( int i = 0; i < 26; i++) { if (25 != i) { int avg = total / (26 - i); //控制震荡范围在动态平均值附近 temp = ran.Next(0, avg * 2); total = total - temp; } else temp = total; templist.Add(temp); } //check int sum = 0; for ( int m = 0; m <templist.Count; m++) { sum = sum + templist[m]; Console.WriteLine(m+ ":" +templist[m]); } Console.WriteLine(sum); Console.ReadKey(); } } |
但是觉得这样并不好,控制了震荡范围,就等于间接控制了随机数字的出现概率,算来算去还是第一种方法和第二种方法好,元芳,你认为呢?
作者:立雪三尺
出处:http://www.cnblogs.com/songsz1/
我滴生命是如此滴辉煌!我滴生活是如此滴灿烂!
关于作者:初出茅庐,职场菜鸟。静如瘫痪,动若癫痫。!姓名不知,生死不详!
如有问题或建议,请多多赐教! 如无,出门左转是政府。
本文版权归作者所有,欢迎转载,不一定非得在显眼处给出原文链接
如有想不开的暴击我
最后严重警告:本文作者真的是名程序员。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述