项目欧拉#21:程序永远运行

时间:2015-12-16 19:14:25

标签: java

我正在尝试使用Project Euler#21:

  

d(n)定义为n的适当除数之和(小于n的数字,均分为n)。

     

如果d(a) = bd(b) = aa ≠ b,那么ab是友好对,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,但这看起来很糟糕。

3 个答案:

答案 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。顺便说一下问题的构建方式,您知道必须找到ab d(a) = bd(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++)
{
  ...
}

这样您只评估唯一对。