【问题标题】:How do I speed up my Amicable number algorithm?如何加快我的友好数算法?
【发布时间】:2017-05-29 22:05:19
【问题描述】:

完成 100,000 的限制(n)需要相当长的时间。

我怀疑问题出在 CalculateAmicable() 上,其中数字变大并且需要更多时间来计算。我可以改变什么让它比这更快?

public static void Main (string[] args)
    {
        CheckAmicable (1, 100000);

    }

    public static int CheckAmicable(int start, int end)
    {
        for (int i = start; i < end; i++) {

            int main = CalculateAmicable (i); //220
            int temp = CalculateAmicable (main); //284
            int compare = CalculateAmicable (temp); //220
            if (compare == main) {
                if (main != temp && temp == i) {
                    Console.WriteLine (main + " = " + temp + " = " + compare + " i: " + i);
                    i = compare + 1;
                }
            }
        }
        return 0;
    }

    public static int CalculateAmicable(int number)
    {
        int total = 0;
        for (int i = 1; i < number; i++) {
            if (number%i == 0){
                total += i;
            }
        }
        return total;
    }

注意:英语不是我的第一语言!

【问题讨论】:

  • CalculateAmicable 中,您可以从 1 循环到 number 的平方根,然后每次将 2 加到总数中。
  • @xzoert 我和你的想法一样,但不认为它有效。要验证,请尝试将数字 9 插入现有方法。我认为相反,如果您从 1 循环到数字值的一半并将总数增加 1,这应该可以工作。
  • 它确实有效。您每次都添加total += i + number / i。您正在对除数求和,而不是得到它们的数量。您需要检查以避免添加 number 本身(使用 1)。
  • 好收获。我正在 7 英寸平板电脑上阅读此内容,并且将 total += i 读取为 total += 1!

标签: c# algorithm optimization


【解决方案1】:

是的,CalculateAmicable 效率低下 - O(N) 复杂 - 所以你有 fo N 测试 1e5 * 1e5 == 1e10 - 100 亿 个操作很慢。 将复杂度降低到O(log(N)) 的方案是

  int n = 100000;

  //TODO: implement IList<int> GetPrimesUpTo(int) yourself
  var primes = GetPrimesUpTo((int)(Math.Sqrt(n + 1) + 1));

  // Key - number itself, Value - divisors' sum 
  var direct = Enumerable
    .Range(1, n)
    .AsParallel()
    .ToDictionary(index => index, 
                  index => GetDivisorsSum(index, primes) - index);

  var result = Enumerable
    .Range(1, n)
    .Where(x => x < direct[x] && 
                direct.ContainsKey((int) direct[x]) &&
                direct[(int) direct[x]] == x)
    .Select(x => $"{x,5}, {direct[x],5}");

  Console.Write(string.Join(Environment.NewLine, result));

结果在一秒钟内(Core i7 3.2Ghz .Net 4.6 IA-64):

  220,   284
 1184,  1210
 2620,  2924
 5020,  5564
 6232,  6368
10744, 10856
12285, 14595
17296, 18416
63020, 76084
66928, 66992
67095, 71145
69615, 87633
79750, 88730

详情GetDivisorsSum:

private static long GetDivisorsSum(long value, IList<int> primes) {
  HashSet<long> hs = new HashSet<long>();

  IList<long> divisors = GetPrimeDivisors(value, primes);

  ulong n = (ulong) 1;
  n = n << divisors.Count;

  long result = 1;

  for (ulong i = 1; i < n; ++i) {
    ulong v = i;
    long p = 1;

    for (int j = 0; j < divisors.Count; ++j) {
      if ((v % 2) != 0)
        p *= divisors[j];

      v = v / 2;
    }

    if (hs.Contains(p))
      continue;

    result += p;

    hs.Add(p);
  }

  return result;
}

还有GetPrimeDivisors

private static IList<long> GetPrimeDivisors(long value, IList<int> primes) {
  List<long> results = new List<long>();

  int v = 0;
  long threshould = (long) (Math.Sqrt(value) + 1);

  for (int i = 0; i < primes.Count; ++i) {
    v = primes[i];

    if (v > threshould)
      break;

    if ((value % v) != 0)
      continue;

    while ((value % v) == 0) {
      value = value / v;

      results.Add(v);
    }

    threshould = (long) (Math.Sqrt(value) + 1);
  }

  if (value > 1)
    results.Add(value);

  return results;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-26
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    相关资源
    最近更新 更多