【问题标题】:Calculate amicable numbers efficiently to a very high upper limit in java在java中有效地计算友好数字到一个非常高的上限
【发布时间】:2021-03-16 03:12:59
【问题描述】:

我有一个程序可以计算出达到一定限度的友好数字对并将它们打印到标准输出。仅供参考 - 如果每个数字都等于另一个数字的适当除数之和(例如,220 和 284),则两个数字是友好的。

我的程序在前 8 对左右运行良好,但问题是当限制达到非常高的数字(例如 500 万)时,它会变得非常慢。有没有办法优化它,使其计算得更快? 这是我的代码

public class Amicable{
    private static int num1, num2, limit = 5000000;
    public static void main(String[] args){
        for(int i = 1; i < limit;i++){
            num1= sumOfDivisors(i);
            num2 = sumOfDivisors(i)
            if(num1 == num2){
                    System.out.println(num2 + " " + num1);
                }
            }
        }
    }
    private static int sumOfDivisors(int n){
        int sum = 0;
        for(int i=1;i<=n/2;i++){
            if(n%i==0){
                sum =sum+i;
            }
        }
        return sum;
    }
}

【问题讨论】:

  • 只是一个想法,你可以对偶数使用递归 - 继续分成两半,直到它不是偶数..例如220 -> 110 -> 55,所以计算出 55、2x55 和 4x55 的因数 [不包括在定义中].. 我想其他素数也可以这样做

标签: java performance


【解决方案1】:

你应该让最里面的循环高效。

static int sumOfDivisors(int n) {
    int sum = 1;
    int max = (int) Math.sqrt(n);
    if (n > 1 && max * max == n) // in case of n is perfect square number
        sum -= max;
    for (int i = 2; i <= max; ++i)
        if (n % i == 0)
            sum += i + n / i;
    return sum;
}

如果n = 220,它可以被2整除,所以可以同时加2和110。但是如果n是一个完全平方数,比如n = 100,它可以被10整除,但是如果10和10相加就会重复,所以要提前减去。

limit =2000000 的输出:

220 284
1184 1210
2620 2924
5020 5564
6232 6368
10744 10856
12285 14595
17296 18416
63020 76084
66928 66992
.....
1468324 1749212
1511930 1598470
1669910 2062570
1798875 1870245

在我的电脑上花了 7 秒。

【讨论】:

  • 谢谢!这行得通,但是如果说我不想将 1 作为适当的除数包括在内,我是否只需将 sum 更改为 0。它是否仍会发回具有相同适当除数之和的数字而不将 1 计为适当的除数?
【解决方案2】:

您可以将“if(i

【讨论】:

  • 除了 i
  • 对不起,我拼错了,应该是“num1
  • 那么为了让这个答案很好,请解释为什么这会提高速度。
【解决方案3】:

这是一种更高效、更快捷的方法。

在 sumOfDivisors 内:

  • 从 2 开始迭代。(没什么大不了的)并在返回时间加 1,总和为 1 也是一个除数。

  • 修改循环终止逻辑,i

    对于任何数字“num”,其所有除数始终小于等于“num/2”,所有素因数始终小于等于 sqrt(num)。

  • 如果 n%i == 0,则检查两个除数是否相等。相等则取一,不相等则取一。

private static int sumOfDivisors(int n) {
    int sum = 0;
    int sqrt = Math.sqrt(n);
    for (int i = 2; i <= sqrt; i++) {
        if (n % i == 0) {
            if(i == (n/i))
                sum = sum + i;
            else
                sum += (i+ n/i);
        }
    }
    return sum+1;
}

【讨论】:

  • 为每个循环计算Math.sqrt(n)是低效的。
  • n 对于每个循环都是不同的,那么我们该如何管理呢?尽管 Math.sqrt 成本高昂,但它减少了许多不必要的操作,并提高了整体性能。是的,我们可以记住 sqrt 的值,这可以提高它的性能。
  • 在编写此代码时,n 对于循环的每次迭代都具有相同的值。因此,替换为int i = 2, m = (int) Math.sqrt(n) + 1; i &lt;= m; i++,避免重复计算n的平方根的成本。
  • 感谢你们两位的改进,我已经更新了我的代码块。
猜你喜欢
  • 1970-01-01
  • 2019-01-15
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-18
相关资源
最近更新 更多