如何思索算法(一)
今天在刷网页的时候发现一个求最小公倍数的问题,感觉颇有意思就拿来看,有意思的不是题目而是我在解决该问题时所做的探索。
题目:求能被1~n所有数整除的最小正数(最小公倍数)。
一眼瞅去一点思路都没有,只想从1*2*3*4*.....*n的结果就一定能够除所有1到n。但是这个数不是最小的,因为存在因子冗余。比如说:能够被8整除的数一定可以被2整除,被9整除的数一定可以被3整除。下面来一步一步的思考如何解决该问题:
考虑1~10的情况:1、2、3、4、5、6、7、8、9、10。设置一个因子数组factor(10)。
首先,去除1,这一点毫无疑问,不解释。
其次,选出素数2、3、5、7放入factor中。这些数一定是所求最小整数的因子,因为它们只能被1和自身整除。此时,factor[(10)={2、3、5、7}
然后,剩下的数有4、6、8、9、10。分解因子:
4=2*2;
6=2*3;
8=2*4;
9=3*3;
10=2*5;
分析,去掉6和10,因为2*3=6,2*5=10,因为在我们的素数中存在2、3、5。如果要能被9整除则必须包含两个3,向factor中添加一个3。最后考虑4和8,如果要能被8整除则必须有2*4,因为求最小的整数,所以因子小的保留。到此,factor(10)={2、3、5、7、4、3}问题得到解决该最小整数是2*3*4*5*7*3=2520。
考虑1~20的情况:1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17、18、19、20。设置一个因子数组factor。
首先,从大到小缩减范围。能够被11、12、13、14、15、16、17、18、19、20整除的数一定可以整除11、12、13、14、15、16、17、18、19、20。
其次,选出11~20中的素数加入factor中。此时factor(20)={11、13、17、19}。
然后,剩下的数有12、14、15、16、18、20。分解因子:
12=2*6---->2*2*3
14=2*7
15=3*5
16=2*8----->2*2*4------->2*2*2*2
20=2*10---->2*2*5
对因子进行删重去除冗余,剩余2、2、5、7、4、3、3加入数组factor,此时factor(20)={2、2、5、7、4、3、3、11、13、17、19}
结果为2*5*7*4*3*3*11*13*17*19=232792560。
Factor(10)={2、3、5、7、4、3}
factor(20)={2、5、7、4、3、3、11、13、17、19、2}
发现规律了吗?
现在我可以立即写出1~30的最小公倍数的因子
Factor(30)={2,3,5,7,11,13,17,19,23,29,2,2,2,3,3,5}。
红色的是1~30的所有素数。为什么在后边添加了2,2,2,3,3,5呢?因为在1~30中包含了16,25,27这三个特别数字,为什么特别,因为它们需要2的4次方,5的2次方和3的3次方得到。也就意味着在factor中必须出现4次,2次和3次。
因此很容易得到该问题的算法实现了:
1、计算1~n的所有素数{a1,a2,a3........an}
2、依次从小到大检查素数的幂次方,记录下不大于n的幂次m。
3、将该素数的m次方与其他素数累乘。
4、重复2、3,直到所有素数都被检查完。
下面给出该问题的java实现
public static long 最小公倍数(int n){ long result = 1; int primes[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97}; List list = new ArrayList(); List exlist = new ArrayList(); for(int i=0;i<primes.length;i++){ if(primes[i]<n){ list.add(primes[i]); } } for(int i=0;i<list.size();i++){ int t =(Integer) list.get(i); int q = 0; int multis=t; for(;;q++){ multis*=t; if(multis>n){ for(int j=0;j<q;j++){ exlist.add(t); } break; } } } for(int i=0;i<list.size();i++){ int t =(Integer) list.get(i); result*=t; } for(int i=0;i<exlist.size();i++){ int t =(Integer) exlist.get(i); result*=t; } System.out.println(list.toString()); System.out.println(exlist.toString()); return result; }
其实,任何自然数都可以通过素数的n次幂的乘积表示,即
N=(2^n)*(3^n)*(5^n)*(7^n)*(11^n)*(13^n)*(17^n)*............
这也正是程序实现的数学支持。如果知道该公式,我们就可以省却很多功夫,不用一点一点自己去总结发现规律,一下就能知道如何解决,但是思维的延伸却不能这样懒省事,要积极的去进行探索才能迸出创造的火花。