我再一次对Project Euler挑战感到高兴,我注意到我的12号解决方案是我在~593.275 ms
每个月的最慢速度之一。这是我在~1254.593 ms
每个月的第10号解决方案的第二位。我的所有其他答案在3 ms
下的最佳状态下运行时间不到1 ms
。
我的Problem 12 Java解决方案:
主要():
int index = 1;
long currTriangleNum = 1;
while (numDivisors(currTriangleNum) <= 500) {
index++;
currTriangleNum += index;
}
System.out.println(currTriangleNum);
numDivisors():
public static int numDivisors(long num) {
int numTotal = 0;
if (num > 1)
if (num % 2 == 0) {
for (long i = 1; i * i <= num; i++)
if (num % i == 0)
numTotal+=2;
} else {
// halves the time for odd numbers
for (long i = 1; i * i <= num; i+=2)
if (num % i == 0)
numTotal+=2;
}
else if (num == 0)
return 0;
else if (num == 1)
return 1;
else (num < 0)
return numDivisors(num *= -1);
return numTotal;
}
环顾解决方案论坛,有些人发现these formulas(n =(p ^ a)(q ^ b)(r ^ c)...&amp; d(n)=(a + 1) )(b + 1)(c + 1)...)为他们工作,但我个人不知道它是如何更快;也许是手动更快,但不是在程序中。
基本的思考过程如下:
我们想要计算48中除数的数量。通过查看下面的因子树,我们可以推断出48 = (2^4)(3^1)
[n =(p ^ a)(q ^ b)(r ^ c)。 ..]。
48
/ \
2 24
/ \
2 12
/ \
2 06
/ \
2 3
知道这一点,我们构造公式d(48) = (4+1)(1+1)
[d(n)=(a + 1)(b + 1)(c + 1)...]以确定48有10个因子。
d(n) = (a+1)(b+1)(c+1)...
d(48) = (4+1)(1+1)
d(48) = (5)(2)
d(48) = 10
如何优化代码?这些配方是最好的解决方案吗?我觉得找到所有主要因素,然后实施公式所需的时间比我已有的程序要长。
非常感谢,
瑞斯蒂昂
修改
在任何人开始发布链接之前,我已经在没有任何运气的情况下查看了类似的问题 - 我只是想不起他们的方法的实现会比我现有的更快。
EDIT2:
我在Eratosthenes筛选中的第二次尝试(针对问题10):
int p = 3, n = 2000000;
long total = 0;
boolean[] sieve = new boolean[n];
for (int i = 3; i < n; i += 2)
sieve[i] = true;
sieve[2] = true;
while (p * p < n) {
for (int i = p; i < n; i++)
if (sieve[i] && (i % p) == 0)
sieve[i] = false;
p++;
while (!sieve[p])
p++;
}
for (int i = 0; i < n; i++)
if (sieve[i])
total += i;
System.out.println(total);
在~985.399 ms
运行 - 不比其他方法快,但尚未优化。然而,它有效。
答案 0 :(得分:10)
使用基础数学结构,这将大大改变程序的运行时间。顺便说一下,这也适用于问题10;如果你不能在几毫秒内完成,你就使用了一种效率极低的算法。事实上,我建议你先解决问题10,因为问题12建立在它上面。
我将为下面的问题12提供更好的算法,但首先,这是一个观察,应该显着加快你的程序。如果两个数字x和y是互质的(即它们除1之外没有公约数),则d(x·y)= d(x)·d(y)。特别地,对于三角形数,d(n·(n + 1))= d(n)·d(n + 1)。因此,不是迭代三角形数n·(n + 1),而是遍历n:这将显着减小传递给d(n)的参数的大小。
如果你进行了这种优化,你会注意到你连续两次计算d(n)(一次为d((n-1)+1),一次为d(n))。这表明缓存d的结果是个好主意。下面的算法可以实现它,但也可以自下而上计算自上而下,这样更有效,因为乘法比分解要快得多。
Problem 10可以通过sieve of Eratosthenes的简单应用来解决。填写一个大小为2000000的布尔数组(即位矢量),如果sieve[i]==true
为素数则用i
填充;然后总结sieve[i]==true
。
Problem 12可以通过Eratosthenes筛子的推广来解决。而不是使sieve[i]
表示i
是否为素数的布尔值,而是使其成为一个数字,表示非素数的方式,即除数的数量。 i
。可以很容易地修改Eratosthenes的基本筛子:不是将sieve[x*y]
设置为false
,而是添加1。
随后的一些项目Euler问题也受益于类似的方法。
您可能遇到的一个问题是,在问题12中,不清楚何时停止计算筛子。你可以通过两种方式解决它:
1.按需要按块计算筛子,这本身就是一项有价值的编程练习(这将需要更复杂的代码,第二种方法)
2.或者从高估一个界限开始:找到一个超过500个除数的三角形数字,你知道你将在那个数字之前或之前停止。
如果你意识到你只需要关心奇数就可以获得更多的时间,因为如果n是奇数,则d(2 ^ k·n)=(k + 1)·d(n),并找到k和n只给出(2 ^ k·n)在二进制计算机上很快。我将把优化的细节留作练习。
答案 1 :(得分:0)
您是否考虑过进入主要因素,并跟踪素数以便您不必重新计算它们?
答案 2 :(得分:0)
我刚才做过这个,所以我不记得所有的优化,这里有一些:
*如果您不知道“规则”,则将其视为概率问题。例如,你可以在咖啡中添加四种风味,你有多少选择?