这是一个性能合理的程序。我省略了一些优化以保持简单和有教育意义。
#include <stdio.h>
/* This is a program to find perfect numbers or "almost perfect" numbers.
(The sum of the proper divisors of an almost perfect number n is n-1, so
the sum of all the divisors is 2*n-1. The "target" object can be set as
desired to find numbers whose divisors sum to a specific total.) It works
by factoring candidates and computing the product of:
1 + p + p^2 + … + p^e
for each prime p, where e is the greatest integer such that p^e divides n.
(Observe that, notionally, this product can be computed over every prime,
all of the infinitely many primes, because, for any prime that does not
divide n, e is 0, and then 1 + p + p^2 + … + p^e is just 1, and multiplying
the product by 1 does not affect it.)
Observe that each divisor of n is a product of some combination of the
primes that divide n, each prime with some multiplicity. Each of the 1 + p
+ p^2 + … + p^e factors in our product contains its prime p in each
multiplicity from 0 to its e, inclusive. When all of these factors are
written as polynomials and multiplied, the terms of the expanded polynomial
contain each combination of the primes that divide n with each combination
of multiplicities. Therefore, it is a list of all the divisors of n, and
they are added together, so the polynomial equals the sum of the divisors
of n (including 1 and n). For example, with 72, 72 = 2^3 * 3^2, so our
product is (1 + 2 + 4 + 8) * (1 + 3 + 9) = 1 + 3 + 9 + 2 + 6 + 18 + 4 + 12
+ 36 + 8 + 24 + 72 = 195, which is the sum of the divisors of 72.
The program finds primes as it goes.
Some things in it need to be verified, such as how far we can factor given
the highest prime number we have remembered, especially as different
control paths are taken.
*/
/* Set a number of primes to remember. This limits how far we can test as it
limits our ability to factor numbers.
*/
#define AP 1000 // Number of elements Allocated for Primes.
int main(void)
{
unsigned Primes[AP];
unsigned NP = 0; // Number of filled elements in Primes.
unsigned Limit = 4; // Current limit on n we know we can support.
/* Test integers from 1 to Limit. Limit will be increased as we find
more primes. It will stop increasing when our Primes array is full.
*/
for (unsigned n = 1; n <= Limit; ++n)
{
// Occasionally print a message to let user know of our progress.
if (n % (1u<<22) == 0) printf("(Now at %u.)\n", n);
/* Set the target sum. If it is 2*n, this program finds perfect
numbers. If it is 2*n-1, this program finds "almost perfect"
numbers.
*/
unsigned target = 2*n-1;
/* t is used as a temporary value as we factor n. It starts at n and
is divided by each prime factor found.
Product records the product we are building.
*/
unsigned t = n, Product = 1;
/* i indexes the Primes array and of course starts at 0.
p is the current prime we are testing and starts at the first
prime, 2.
Once t is less than p*p, the loop stops, because then either t is
1 or some prime number. (Any composite number less than or equal
to p*p has a factor less than or equal to p, so we would have found
it and divided t by it.)
*/
for (unsigned i = 0, p = 2; p*p <= t; p = Primes[++i])
{
// Start the sum with p^0, which is 1.
unsigned Sum = 1;
/* For each time p divides t, grow the Sum, forming 1+p+p^2+…p^e.
For example, the Sum starts at 1. If p divides t, then
"Sum = 1 + Sum*p" sets it to 1+p. If p divides t again, this
sets it to 1+(1+p)*p = 1+p+p^2, and so on.
*/
for (; t % p == 0; t /= p)
Sum = 1 + Sum*p;
// Multiply the sum into the product.
Product *= Sum;
}
// If t has not been reduced to 1, it is the last prime factor of n.
if (1 < t)
{
// If there were no other factors, we have a new prime.
if (Product == 1)
{
/* If we still have space, remember the new prime and increase
Limit. Knowing primes up to n lets us test numbers up to
n*n because any number up to n*n with a factor f larger
than n also has a factor smaller than n, namely that number
divided by f.
*/
if (NP < AP)
{
Primes[NP++] = n;
Limit = n*n;
}
}
// Multiply the series for the last factor into Product.
Product *= 1+t;
}
// If our criterion for n is met, report it.
if (Product == target)
printf("Found %u.\n", n);
}
printf("Tested numbers up to and including %u.\n", Limit);
}