繁体   English   中英

所有适当除数的总和

[英]Summation of all proper divisors

我已经解决了一个问题,说:
给定自然数n(1 <= n <= 500000),请输出其所有适当除数的总和。

定义:自然数的适当除数是严格小于该数字的除数。

例如,数字20有5个合适的除数:1、2、4、5、10,除数的总和为:1 + 2 + 4 + 5 + 10 = 22。

输入项

一个整数,表示测试用例的数量(约等于200000),后面紧跟着很多行,每行包含一个介于1到500000(含)和500000之间的整数。

输出量

每行一个整数:分别给出的整数的除数和。

输入样例:

3
2
10
20

样本输出:

1个
8
22

我的代码如下:

/* @BEGIN_OF_SOURCE_CODE */

#include <stdio.h>
#include <stdlib.h>

    int main(int argc, const char * argv[])
    {
        int sum = 0,
        cases = 0,
        i, j, buff;

        scanf("%d", &cases); //Number of tests


        int *n;
        n = (int*) malloc(cases * sizeof(int)); //Defining array for numbers to be tested///////

        for (i = 0; i < cases; i++) {
            scanf("%d", &n[i]);
        }
        for (i = 0; i < cases; i++ ) {
            buff = n[i] / 2;
            if (n[i] == 1) {
                sum = -1;
            }


            if (!(n[i] & 1)) {
                for (j = 2; j < buff; j++) {
                    if (n[i] % j == 0) {
                        sum += n[i] / j + j;
                        buff /= j;
                    }
                }
            }


            else {
                for (j = 3; j < buff; j += 2) {
                    if (n[i] % j == 0) {
                        if (n[i] / j == j) { sum += j; break; }
                        else sum += n[i] / j + j;
                    }
                    buff /= j;
                }
             }
            printf("%d\n", ++sum);
            sum = 0;
        }
        return 0;
    }
    /* @END_OF_SOURCE_CODE */

但这还不够快。 有什么建议么?

我更新了下面的代码以尽快终止。 在MacBookPro6,1(2.66 GHz Intel Core i7)上运行它(从1到500,000的所有整数)需要不到半秒的时间,而MacBookPro是使用带-O3的Apple GCC 4.2.1编译的。

它使用在的属性部分σX(N)Wikipedia页面为除数函数 使用预先计算的素数列表可以使其变得更快。 (需要126个来支持最多500,000个输入,这将时间减少到不到四分之一秒。)还可以消除某些划分,但会稍微使代码混乱。

//  Return the least power of a that does not divide x.
static unsigned int LeastPower(unsigned int a, unsigned int x)
{
    unsigned int b = a;
    while (x % b == 0)
        b *= a;
    return b;
}


//  Return the sum of the proper divisors of x.
static unsigned int SumDivisors(unsigned int x)
{
    unsigned int t = x;
    unsigned int result = 1;

    //  Handle two specially.
    {
        unsigned int p = LeastPower(2, t);
        result *= p-1;
        t /= p/2;
    }

    //  Handle odd factors.
    for (unsigned int i = 3; i*i <= t; i += 2)
    {
        unsigned int p = LeastPower(i, t);
        result *= (p-1) / (i-1);
        t /= p/i;
    }

    //  At this point, t must be one or prime.
    if (1 < t)
        result *= 1+t;

    return result - x;
}

您不必分配空间。 只是一行一行地做。 每行都有一个O(n ^ 1/2)算法。

#include <iostream>
using std::cout; using std::endl; using std::cin;

int main() {
   int count, number;
   cin >> count;
   for (int i = 0; i < count; ++i) {
      cin >> number;
      int sum = 1;
      for ( int j = 2; j * j <= number; ++j ) {
         if ( number % j == 0 ) {
            sum += j;
            sum += number / j;
         }
         if ( j * j == number ) sum -= j; // recalculate twice
      }
      cout << sum << endl;
   }
}

这是200,000个测试用例的运行时

real    0m55.420s
user    0m0.016s
sys     0m16.124s

我将从根本不将数字存储在数组中开始。 您不需要-只需读取值,对其进行处理并输出结果。 编译器可能不会意识到在整个循环中n[i]是相同的值,并且没有其他修改。

在我看来,逻辑并不十分清楚。 if (n[i] == 1) { sum = 1} else ...比设置sum = -1更有意义。

您也许还可以保留一个“常见因素”列表( http://en.wikipedia.org/wiki/Memoization ),这样您就不必多次重新计算同一件事。 [如果知道某事物的因子为24,那么它也具有2、3、4、6和8。

我回答了关于stackoverflow的类似问题

有一种执行速度更快的算法,该算法基于使用素因子分解的除数之和公式

首先,构造一个质数表,使最后一个质数平方小于数字的上限。 然后将公式应用于每个条目。 如果数字写为

n = a1^p1 * a1^p2 *... *an^pn

查找给定数n的总和的复杂度为

p1+p2+...+pn = roughtly log(n)

这比第一个优化的复杂度O(sqrt(n))更好,后者可以尽早停止循环

假设您有一种方法可以相对快速地计算素数。 这可能是一次性的前期活动,以最大输入值的平方根为边界。 在这种情况下,您已经知道最大输入值(500000)的界限,因此您可以简单地将素数表硬编码到程序中。

static unsigned P[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701
};

static int P_COUNT = sizeof(P)/sizeof(*P);

现在,从素数中,对于每个输入值,您可以:

  • 计算素数分解
  • 计算每个素数的幂和的乘积。

这将导致除数之和。 从总和中减去输入值以获得适当除数的总和。 这两个步骤可以组合成一个循环。

该算法之所以有效,是因为多项式相乘自然会导致多项式项的所有组合之和相乘。 在每个多项式项由除以输入的质数的幂组成的情况下,乘以项的组合构成除数。 该算法速度很快,在Core i3或更佳的处理器上,应该能够在不到1秒的时间内处理间隔[1,500000]中的500000个数字。

以下功能实现了上述方法。

unsigned compute (unsigned n) {
    unsigned sum = 1;
    unsigned x = n;
    for (int i = 0; i < P_COUNT; ++i) {
        if (P[i] > x / P[i]) break;    /* remaining primes won't divide x */
        if (x % P[i] == 0) {           /* P[i] is a divisor of n */
            unsigned sub = P[i] + 1;   /* add in power of P[i] */
            x /= P[i];                 /* reduce x by P[i] */
            while (x % P[i] == 0) {    /* while P[i] still divides x */
                x /= P[i];             /* reduce x */
                sub = sub * P[i] + 1;  /* add by another power of P[i] */
            }
            sum *= sub;                /* product of sums */
        }
    }
    if (x > 1) sum *= x + 1;           /* if x > 1, then x is prime */
    return sum - n;
}

此代码的复杂度为O(n * log(n))。 但是您可以在恒定时间内输出所需的答案。

int ans[500000 + 10], m = 500000;

int f(){
    for(int i = 1; i <= m; i++){
       for(int j = i + i; j <= m; j += i){
           ans[j] += i;
       }
     }
}

在这里, ans是一个数组,其中包含从2到m的适当除数之和。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM