对友好数字

时间:2019-03-14 08:15:26

标签: c# algorithm math

我有一个任务来查找成对的友好数字,并且我已经解决了。我的解决方案效率不高,因此请帮助我加快算法的速度。

亲和数是两个不同的数,因此彼此相关,以使每个数的适当除数之和等于另一个数。最小的一对友好数字是(220,284)。之所以友好,是因为220的适当除数是1、2、4、5、10、11、20、22、44、55和110,总和为284;并且284的适当除数分别是1、2、4、71和142,总和为220。

任务:两个long数字,并找到它们之间的第一个友好数字。令s(n)为n的适当除数之和:

例如:

s(10) = 1 + 2 + 5 = 8
s(11) = 1
s(12) = 1 + 2 + 3 + 4 + 6 = 16

如果s(firstlong) == s(secondLong)是友善的数字

我的代码:

public static IEnumerable<long> Ranger(long length) {
  for (long i = 1; i <= length; i++) {
    yield return i;
  }
}

public static IEnumerable<long> GetDivisors(long num) {
  return from a in Ranger(num/2)
    where num % a == 0
    select a;
}

public static string FindAmicable(long start, long limit) {
  long numberN = 0;
  long numberM = 0;

  for (long n = start; n <= limit; n++) {
    long sumN = GetDivisors(n).Sum();      
    long m = sumN;

    long sumM = GetDivisors(m).Sum();

    if (n == sumM ) {
      numberN = n;      
      numberM = m;
      break;
    }
  }

  return $"First amicable numbers: {numberN} and {numberM}";
}

2 个答案:

答案 0 :(得分:0)

我通常不写C#,因此,我将介绍C#-madeup-psuedo代码的一种改进,而不是偶然地遇到一些不连贯的C#意大利面条。

问题似乎出在您的GetDivisors函数中。相对于每个除数O(n)的线性n时间为O(sqrt(n))。诀窍是只除以平方根,然后从中推断出其他因素。

GetDivisors(num) {
    // same as before, but up to sqrt(num), plus a bit for floating point error
    yield return a     in Ranger((long)sqrt(num + 0.5)) where num % a == 0

    if ((long)sqrt(num + 0.5) ** 2 == num) { // perfect square, exists
        num -= 1 // don't count it twice
    }

    // repeat, but return the "other half"-  num / a  instead of  a
    yield return num/a in Ranger((long)sqrt(num + 0.5)) where num % a == 0
}

这会将您这部分的复杂度从O(n)降低到O(sqrt(n)),这应该可以显着提高速度。

答案 1 :(得分:0)

有一个简单的公式可以得出一个数的除数之和,知道其素数分解:

 let x = p1^a1 * ... * pn^an, where pi is a prime for all i
 sum of divisors = (1+p1+...+p1^a1) * ... (1+pn+...+pn^an)
                 = (1-p1^(a1+1))/(1-p1) * ... ((1-pn^(an+1))/(1-pn)

为了进行素数分解,必须计算所有素数,直到搜索范围内最大值的平方根。这可以很容易地使用Erathostenes的筛子完成。