C#编程之神奇程序找数
C#编程之神奇程序找数
问题1:这个程序要找的是符合什么条件的数?
问题2:这样的数存在么?符合这一条件的最小的数是什么?
问题3:在电脑上运行这一程序,你估计多长时间才能输出第一个结果?时间精确到分钟(电脑:单核CPU 4.0G Hz,内存和硬盘等资源充足)。
问题4:在多核电脑上如何提高这一程序的运行效率?
(注:该程序、用C#语言编写,但是只要有C语言基础完全没有阅读压力,如果对部分语句不懂请自行查询)
将上述问题结果写到博客上,截止时间本周日(3月19日)晚8时
using System;
using System.Collections.Generic;
using System.Text;
namespace FindTheNumber
{
class Program
{
static void Main(string[] args)
{
int [] rg =
{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
20,21,22,23,24,25,26,27,28,29,30,31};
for (Int64 i = 1; i < Int64.MaxValue; i++)
{
int hit = 0;
int hit1 = -1;
int hit2 = -1;
for (int j = 0; (j < rg.Length) && (hit <=2) ; j++)
{
if ((i % rg[j]) != 0)
{
hit++;
if (hit == 1)
{
hit1 = j;
}
else if (hit == 2)
{
hit2 = j;
}
else
break;
}
}
if ((hit == 2)&& (hit1+1==hit2))
{
Console.WriteLine("found {0}", i);
}
}
}
}
}
首先,对于上述的几个问题我先不做回答,我先谈一下自己对于这个程序的理解,这个程序我自己运行了差不多三个小时,但是并没有出来结果,我想这应该是大多数同学共同的问题。第一眼看到这段代码的时候,我以为会在一分钟以内出来结果,然而第一次运行直接宣告了我认知上的错误。这说明这个程序有自己的内部原理,并不是我们常规情况下所遇到的很快就会出现结果的程序。运行没有得到任何的结果,但我并没有就此放弃,通过在稿纸上的模拟运算,我基本上了解了这段代码的目的。这段代码看起来并不难,但实际上它的运算量是相当庞大的。说完自己的理解,接下来我就对于这几个问题做简单分析解答。
问题1:
对于第一个问题,我的理解是寻找一个变量i,而这个i满足的条件应该是这几个条件:
条件一:(i % rg[j]!= 0)&&(i % rg[j+1]!= 0)&&(i % rg[j+2]== 0),也就是说对于三个连续的数a,b,c;i不能整出a和b,但可以整除c,比如当a=7,b=8,c=9;此时当i=54时满足这个条件;这是我分析的第一个条件。
条件二:在条件一的基础上还必须满足(hit == 2)&& (hit1+1==hit2)这个条件,此时这个数的范围就可以进一步缩小了,因为hit==2,这说明这个数只有不能被rg[j]和rg[j+1]整除,它可以被除rg[j]和rg[j+1]之外的所有数整除。
问题二:
1、对于这个问题,我们应该注意到问题1中的两个条件,因为通过问题1已经分析到rg[j]和rg[j+1]是两个连续的数,那么我们可以确定rg[j]和 rg[j+1]不小于16,以为如果rg[j]一旦小于16,那么rg[j]*2也在rg[]数组内,这样肯定是不满足(hit == 2)&& (hit1+1==hit2)这个条件的。最后得到的hit结果必然大于2。
2、rg[j]和rg[j+1]这两个连续的数也不能由rg[]数组中其他成员变量相乘得到,如果rg[j]和rg[j+1]中任意一个数可由数组中两个数相乘得到,那么该数组中将至少有四个数可以被i整除,也不满足(hit == 2)&& (hit1+1==hit2)这个条件。
3、结合以上两点我们将满足上述条件的数筛选出来{17,19,23,29,31},那么我们需要寻找的rg[j]和rg[j+1]中肯定有一个数在这个数组当中,而且我们完全可以分析得到这两个数是一奇一偶,那我们考虑乘积最小且必须满足第一点的情况,那就是rg[j]和rg[j+1]为16和17,与此同时,我们也考虑17和18,我们分析一下这两组数的区别,不能被16和17整除,但需要被数组内其他所有数整除,虽然16可以由2和8相乘得到,但2是8的因子,所以只要i能整除8,就必能整除2,这就是说i应该可以被8整除但不能被16整除。如果我们考虑18,那么i应该是可以被2和9整除的,但却不能被18整除,这显然是矛盾的。所以rg[j]和rg[j+1]为16和17。
4、以上这几点分析都是个人观点,而最后的答案我自己感觉应该是一个比较大的数,其实我个人观点是通过计算缩小i取值范围之后,可以从高位依次向下取值,这样可以更快的接近结果。具体答案我们可以参考CSDN上边也有大神的具体讲解,这里我对答案做一个引用i=8*25*27*7*11*13*19*23*29*31=2123581660200,之所以考虑到8,25,27,而不考虑除17之外的所有素数,能被27整除的数一定能被3和9整除,反之就不一定了。同理我们可以理解其他情况。
问题三:
对于这个问题,我自己的想法是考虑程序执行一次外循环,大约执行平均10次左右内循环,那么我们就需要计算执行着一次循环所需的时间,当然我自己确实没有什么更好的方法,参考网上的计算方法:我们可以假设该原子操作需要120个时钟周期。因此4GHz的CPU在1秒内能跑4*10^9 / 120= 3.33*10^7 即1670万次原子操作。对于外层循环执行了2123581660200次,内层循环取决于i的情况,这里我们粗略估计,内层循环平均可以跑10次,通过这样的计算方式我们大致可以得出总共执行的操作约为2123581660200/3.33*10^7=6.37*10^5秒,也就是大约10616分钟。
问题四:
首先多核运行比单核要快些,虽然系统说是可多任务运行,但对于单核来说实际上任务在CPU上还是串行处理,而多核就可以由多核来实现并行处理,总体运行速度当然也得到提高。这样我们就可以对程序进行并行运算,我的想法是将数值范围缩小到2...31相乘然后除去特殊数,并且让i从高位数开始计算。这样可能会提高程序效率。那么如果以自己电脑作为参照,我的笔记本在内核版本是占优为双核,但是CPU频率2.5GHZ,按照这些来进行计算,并且考虑对于算法的优化,那么这个时间大约为2123581660200/(2.5*10^9 / 200)= 16984分钟。那么如果是一个双核4GHZ的CPU,这个时间应该将减少大约一半。
总结:
这次的作业如果要说学到了什么,那我就觉得我学到了如何去分析一个程序的条件以及如何把复杂的问题转换为简单,通过一层一层的分析,最终接近了问题的答案,这次的作业有很多地方是参照大神们的方法进行理解和求解的,当然也有自己思考为什么要这么去做?感觉这次的作业应该是对于问题求解的方法的思考,如果我们直接考虑问题的答案,那么我们估计很难通过程序运行得到最终的结果。如果方法得当结果应该自然就可以得到了,至于思考的结果应该是仁者见仁智者见智了,当然具体我们可以参考这篇文章http://blog.csdn.net/zhuhuiby/article/details/6742980,这篇博客对于作业的前三个问题做了详细的分析以及作答,我们当然想不到这么全面,但是大概的思路是值得我们借鉴和学习的。