我正在尝试使用Project Euler#21:
将
d(n)
定义为n
的适当除数之和(小于n
的数字,均分为n
)。如果
d(a) = b
和d(b) = a
,a ≠ b
,那么a
和b
是友好对,a
和{{{ 1}}被称为友好数字。例如,220的适当除数是1,2,4,5,10,11,20,22,44,55和110;因此
b
。 284的适当除数是1,2,4,71和142;所以d(220) = 284
。评估10000以下所有友好数字的总和。
这是我的尝试:
d(284) = 220
我在main中输出的print语句产生:
public class Problem21 {
public static void main(String[] args) {
int total = 0;
for (int i = 1; i < 10000; i++) {
for (int j = 1; j < 10000; j++) {
if (isAmicablePair(i, j)) {
System.out.println("(i, j) = (" + i + ", " + j + ")");
total += (i + j);
}
}
}
System.out.println("sum is " + total);
}
public static boolean isAmicablePair(int a, int b) {
return (sumOfProperDivisors(a) == b && sumOfProperDivisors(b) == a && a != b);
}
public static int sumOfProperDivisors(int n) {
int total = 0;
for (int i = 1; i <= n / 2; i++) {
if (isProperDivisor(i, n)) {
total += i;
}
}
return total;
}
public static boolean isProperDivisor(int candidate, int n) {
return (n % candidate == 0 && n != candidate);
}
}
1)几分钟后,所以它肯定太慢了。
2)另一个问题是每对都会出现两次,我绝对不想要。我可以把最后的总和除以2,但这看起来很糟糕。
答案 0 :(得分:2)
您只需要为每个数字计算一次除数之和。然后只是比较彼此的答案。这样你只能进行10,000次重计算,而不是10,000 ^ 2次。 O(n)代替O(n ^ 2)。
要做“比较答案”部分:
一种选择是将数字和总和放在HashMap<Integer, Integer>
中,使得键是数字,值是总和。迭代Entry
集。在迭代时,取总和并将其作为键重新用于同一Map
。如果你得到一个条目,那么他们是友好的。
快速草图(即我只是在这里展开):
Map<Integer, Integer> numAndSum = new HashMap<>();
for (int i = 1; i <= 10000; i++) {
numAndSum.put(i, sumOfProperDivisors(i);
}
for (Entry<Integer, Integer> anEntry: numAndSum) {
Integer aNum = anEntry.getKey();
Integer aSum = anEntry.getValue();
if (numAndSum.contains(aSum)) {
System.out.println("Amicable pair: " + aNum + ", " + aSum);
}
}
答案 1 :(得分:2)
大瓶颈是以下循环:
for (int i = 1; i < 10000; i++) {
for (int j = 1; j < 10000; j++) {
if (isAmicablePair(i, j)) {
System.out.println("(i, j) = (" + i + ", " + j + ")");
total += (i + j);
}
}
}
当你不需要时,这会循环超过10000 * 10000的值,这太大了。实际上,您根本不需要j
。顺便说一下问题的构建方式,您知道必须找到a
和b
d(a) = b
和d(b) = a
。
这意味着您需要查找a
(和d(d(a)) = a
)的d(a) ≠ a
,从而不再需要变量b
。我们无需通过循环查找所有可能的值来查找b
,我们通过构造知道当a
处于友好对中时,b
将等于d(a)
考虑一下:
for (int i = 1; i < 10000; i++) {
if (isAmicable(i)) {
System.out.println("i = " + i);
total += i;
}
}
方法isAmicable
与您自己的方法略有不同:
public static boolean isAmicablePair(int a) {
int b = sumOfProperDivisors(a);
return b != a && sumOfProperDivisors(b) == a;
}
这实现了检查d(d(a)) = a
。
使用此代码,可以非常快速地找到结果。
答案 2 :(得分:1)
您可以尝试更改内部循环(在isAmicablePair(..)的主枚举中)以使用:
for (j = i+1; j < 10000; j++)
{
...
}
这样您只评估唯一对。