我对Project Euler问题2有两个解决方案,即找到所有偶数Fibonacci数不到400万的总和。
解决方案一(平均需要11,000纳秒):
public class Solution {
static long startTime = System.nanoTime();
static final double UPPER_BOUND = 40e5;
static int sum = 2;
public static int generateFibNumber(int number1, int number2){
int fibNum = number1+ number2;
return fibNum;
}
public static void main( String args[] ) {
int i = 2;
int prevNum = 1;
while(i <= UPPER_BOUND) {
int fibNum = generateFibNumber(prevNum,i);
prevNum = i;
i = fibNum;
if (fibNum%2 == 0){
sum += fibNum;
}
}
long stopTime = System.nanoTime();
long time = stopTime - startTime;
System.out.println("Sum: " + sum);
System.out.println("Time: "+ time);
}
和解决方案二(平均需要14,000纳秒):
public class Solution2 {
static long startTime = System.nanoTime();
final static int UPPER_BOUND = 4_000_000;
static int penultimateTerm = 2;
static int prevTerm = 8;
static int currentTerm = 34;
static int sum = penultimateTerm+ prevTerm;
public static void main( String args[]) {
while (currentTerm <= UPPER_BOUND) {
sum+= currentTerm;
penultimateTerm = prevTerm;
prevTerm = currentTerm;
currentTerm = (4*prevTerm) + penultimateTerm;
}
long stopTime = System.nanoTime();
long time = stopTime - startTime;
System.out.println("Sum: " + sum);
System.out.println("Time: " + time);
}
为什么当我在while循环中执行更少的迭代并且没有if语句时,解决方案2需要更长的时间? 这可以更有效地完成吗?
答案 0 :(得分:4)
第二个版本 更快。正如评论中指出的那样,你的时间不准确。同时对几微秒的函数进行计时也是不可靠的。您应该在循环中运行代码并调整x迭代的总时间,然后使用它来计算每次迭代的平均时间。
此外,我认为显示代码的工作原理可能很有用。请注意,偶数出现在每三个索引处。
1 1 2 3 5 8 13 21 34
^ ^ ^
第二个版本仅直接计算偶数。它是通过从F(n)和F(n-3)计算F(n + 3)的值来实现的。
F(n + 3) = F(n + 2) + F(n + 1)
= F(n + 1) + F(n) + F(n + 1) [1]
= F(n) + F(n - 1) + F(n) + F(n) + F(n - 1) [2]
= F(n) + F(n - 2) + F(n - 3) + F(n) + F(n) + F(n - 1) [3]
= F(n) + F(n) + F(n - 3) + F(n) + F(n) [4]
= 4 * F(n) + F(n - 3)
使用以下身份:
F(n + 2) = F(n + 1) + F(n)
F(n + 1) = F(n) + F(n - 1)
F(n - 1) = F(n - 2) + F(n - 3)
F(n) = F(n - 1) + F(n - 2)
答案 1 :(得分:4)
仅运行一次算法是评估其性能的一种非常不可靠的方法,特别是当时间大约为10ns时。确实,你的第二种方法更快。我重写了你的代码,迭代每个算法100次,并得到了与你截然不同的结果。
代码:
public class Fib {
private static int UPPER_BOUND = 4000000;
private static int ITERS = 100;
public static void main(String[] args) {
long time1, time2;
int sum1 = 0, sum2 = 0;
long startTime = System.nanoTime();
for (int iter = 0; iter < ITERS; ++iter) {
sum1 = sol1();
}
time1 = System.nanoTime() - startTime;
startTime = System.nanoTime();
for (int iter = 0; iter < ITERS; ++iter) {
sum2 = sol2();
}
time2 = System.nanoTime() - startTime;
System.out.println("Time1 = " + time1 + "; sum1 = " + sum1);
System.out.println("Time2 = " + time2 + "; sum2 = " + sum2);
}
private static int sol1() {
int sum = 2;
int i = 2;
int prevNum = 1;
while(i <= UPPER_BOUND) {
int fibNum = generateFibNumber(prevNum,i);
prevNum = i;
i = fibNum;
if (fibNum%2 == 0){
sum += fibNum;
}
}
return sum;
}
private static int sol2() {
int penultimateTerm = 2;
int prevTerm = 8;
int currentTerm = 34;
int sum = penultimateTerm + prevTerm;
while (currentTerm <= UPPER_BOUND) {
sum += currentTerm;
penultimateTerm = prevTerm;
prevTerm = currentTerm;
currentTerm = (prevTerm << 2) + penultimateTerm;
}
return sum;
}
private static int generateFibNumber(int number1, int number2) {
return number1+ number2;
}
}
结果(典型):
时间1 = 189910; sum1 = 4613732
时间2 = 35501; sum2 = 4613732
请注意,在第二种算法中,我使用(4*prevTerm)
更改了(prevTerm << 2)
,这稍快一些。这使时间缩短了约5%。每个测试中仍有很多开销:函数调用并将结果分配给局部变量。但是,通过迭代,您在调用System.nanoTime()
时不会感到沮丧。
请注意,您的第一个代码也使用了double
UPPER_BOUND
,这会让它慢下来。我的代码试图使测试尽可能并行。