Project Euler上第47道题目,链接如下:
http://projecteuler.net/problem=47
最小的两个具有两个不同质数因子的连续整数是:
14 = 2
7
15 = 3
5
最小的三个具有三个不同质数因子的连续整数是:
644 = 2^2
7
23
645 = 3
5
43
646 = 2
17
19.
找出最小的四个具有四个不同质数因子的整数。它们之中的第一个是多少?
******************************************************************************
最容易想到的是用NestWhile,自己写的程序如下:
NestWhile[{Length@FactorInteger[#[[2]] + 1], #[[2]] + 1} &, {2, 209}, (#1[[1]] != 4 || #2[[1]] != 4 || #3[[1]] != 4 || #4[[1]] != 4) &, 4]
运行时间:1.36 sec
***********************************************************************
然后看到直接有内部函数PrimeNu可以算不同质数因子的个数,就改了下:
NestWhile[{PrimeNu[#[[2]] + 1], #[[2]] + 1} &, {2, 209}, (#1[[1]] != 4 || #2[[1]] != 4 || #3[[1]] != 4 || #4[[1]] != 4) &, 4]
结果运行时间变为: 3.9 sec,
慢了3倍。
Trace看了一下,慢的原因是PrimeNu会有一系列的判断过程,不像Length@FactorInteger这般直接。
***************************************************************************
然后在这道题的Forum上看到有人贴出下面NestWhile版本:
NestWhile[# + 1 &, 209, ! (And @@ Thread[4 == Length /@ FactorInteger[# + {0, 1, 2, 3}]]) &]
运行时间是: 3.32 sec
推测这个慢的原因是,相比我自己写的Nest程序,每一次Nest运算,后面的判断都要进行较多的计算
这原因是从下面的改写得到的:
NestWhile[# + 1 &, 209, ! ({4, 4, 4, 4} === Length /@ FactorInteger[# + {0, 1, 2, 3}]) &]
后面的判断减少了计算,运行时间提高到2.85sec
********************************************************************************
但这里最快的却是While循环:
(a = 210; While[Length@FactorInteger[a] != 4 || Length@FactorInteger[a + 1] != 4 || Length@FactorInteger[a + 2] != 4 || Length@FactorInteger[a + 3] != 4, a++]; a)
运行时间:0.81 sec
*********************************************************************************
For循环最慢:
(For[i = 1, Not[{4, 4, 4, 4} === Length /@ FactorInteger[i + {0, 1, 2, 3}]], i++]; i)
2.76 sec
如果Length/@FactorInteger替换成PrimeNu,时间突增到13s。足见,并不是任何内部函数都比自己编的函数要有效率的。。
。记得之前 @xzcyr 好像也发过一个帖子,是说怎么让NestWhile快过While,现在找不到了~
对于这个具体问题,MMA函数式编程有什么改善的方法,使得其运行效率比单纯While循环快?
http://projecteuler.net/problem=47
最小的两个具有两个不同质数因子的连续整数是:
14 = 2

15 = 3

最小的三个具有三个不同质数因子的连续整数是:
644 = 2^2


645 = 3


646 = 2


找出最小的四个具有四个不同质数因子的整数。它们之中的第一个是多少?
******************************************************************************
最容易想到的是用NestWhile,自己写的程序如下:
NestWhile[{Length@FactorInteger[#[[2]] + 1], #[[2]] + 1} &, {2, 209}, (#1[[1]] != 4 || #2[[1]] != 4 || #3[[1]] != 4 || #4[[1]] != 4) &, 4]
运行时间:1.36 sec
***********************************************************************
然后看到直接有内部函数PrimeNu可以算不同质数因子的个数,就改了下:
NestWhile[{PrimeNu[#[[2]] + 1], #[[2]] + 1} &, {2, 209}, (#1[[1]] != 4 || #2[[1]] != 4 || #3[[1]] != 4 || #4[[1]] != 4) &, 4]
结果运行时间变为: 3.9 sec,
慢了3倍。
Trace看了一下,慢的原因是PrimeNu会有一系列的判断过程,不像Length@FactorInteger这般直接。
***************************************************************************
然后在这道题的Forum上看到有人贴出下面NestWhile版本:
NestWhile[# + 1 &, 209, ! (And @@ Thread[4 == Length /@ FactorInteger[# + {0, 1, 2, 3}]]) &]
运行时间是: 3.32 sec
推测这个慢的原因是,相比我自己写的Nest程序,每一次Nest运算,后面的判断都要进行较多的计算
这原因是从下面的改写得到的:
NestWhile[# + 1 &, 209, ! ({4, 4, 4, 4} === Length /@ FactorInteger[# + {0, 1, 2, 3}]) &]
后面的判断减少了计算,运行时间提高到2.85sec
********************************************************************************
但这里最快的却是While循环:
(a = 210; While[Length@FactorInteger[a] != 4 || Length@FactorInteger[a + 1] != 4 || Length@FactorInteger[a + 2] != 4 || Length@FactorInteger[a + 3] != 4, a++]; a)
运行时间:0.81 sec
*********************************************************************************
For循环最慢:
(For[i = 1, Not[{4, 4, 4, 4} === Length /@ FactorInteger[i + {0, 1, 2, 3}]], i++]; i)
2.76 sec
如果Length/@FactorInteger替换成PrimeNu,时间突增到13s。足见,并不是任何内部函数都比自己编的函数要有效率的。。
。记得之前 @xzcyr 好像也发过一个帖子,是说怎么让NestWhile快过While,现在找不到了~
对于这个具体问题,MMA函数式编程有什么改善的方法,使得其运行效率比单纯While循环快?
