我已经解决了一个问题: 给定自然数n(1 <= n <= 500000),请输出所有适当除数的总和。
定义:自然数的适当除数是严格小于数的除数。
e.g。数字20有5个适当的除数:1,2,4,5,10,除数求和为:1 + 2 + 4 + 5 + 10 = 22.
输入
一个整数,表示测试用例的数量(大约等于200000),后面跟着很多行,每个行包含1到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 */
但速度不够快。有什么建议吗?
答案 0 :(得分:7)
我已更新以下代码以更快终止。在MacBookPro6,1(2.66 GHz Intel Core i7)上运行1到500,000的所有整数,用Apple GCC 4.2.1和-O3编译。
它在Wikipedia page for the divisor function的属性部分中使用σ x ( n )的公式。使用预先计算的素数列表可以更快地完成。 (需要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;
}
答案 1 :(得分:2)
您不必分配空间。只是一行一行。 对于每一行,都有一个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
答案 2 :(得分:0)
我首先不将数字存储在数组中。您不需要 - 只需读取值,处理它,然后输出结果。编译器可能没有意识到n[i]
在整个循环中是相同的值,并且没有其他任何修改它。
逻辑对我来说似乎不太清楚。 if (n[i] == 1) { sum = 1} else ...
比设置sum = -1
更有意义。
您也许也可以保留一个“常见因素”列表(http://en.wikipedia.org/wiki/Memoization),这样您就不必多次重新计算同样的事情。 [如果你知道somehing有24因子,那么例如它也有2个,3个,4个,6个和8个。
答案 3 :(得分:0)
I replied to a similar question on stackoverflow
有一种性能更快的算法,该算法基于formula for the sum of divisor使用素数因子中的分解。
首先构建一个primetable,使得最后的素数小于你的数字的上限。然后将公式应用于每个条目。如果数字写为
n = a1^p1 * a1^p2 *... *an^pn
找到给定数字n
的总和的复杂性将是
p1+p2+...+pn = roughtly log(n)
优于早期停止循环的第一个优化的复杂度O(sqrt(n))
答案 4 :(得分:0)
假设您有办法相对快速地计算素数。这可能是一次性的前期活动,受最大输入值的平方根限制。在这种情况下,您已经知道最大输入值(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,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;
}
答案 5 :(得分:0)
此代码在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的适当除数之和。