[英]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。
有一种执行速度更快的算法,该算法基于使用素因子分解的除数之和公式 。
首先,构造一个质数表,使最后一个质数平方小于数字的上限。 然后将公式应用于每个条目。 如果数字写为
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.