问题
After surfing a bit ,I found this code for calculating Euler's phi values in linear time using Sieve of Eratostenes .But failed to understand the main logic used in this code,specially what is done in the inner for loop and idea used in this loop for calculating phi value.It will be helpful if someone helps me understand this code.
#define MAXN 3000000
int phi[MAXN + 1], prime[MAXN/10], sz;
bitset <MAXN + 1> mark;
for (int i = 2; i <= MAXN; i++ ){
if(!mark[i]){
phi[i] = i-1;
prime[sz++]= i;
}
for (int j=0; j<sz && prime[j]*i <= MAXN; j++ ){
mark[prime[j]*i]=1;
if(i%prime[j]==0){
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
else phi[i*prime[j]] = phi[i]*(prime[j]-1 );
}
}
回答1:
The coding is neither here nor there but the algorithm as such is brilliant. It has little to do with the Sieve of Eratosthenes, though. The approach is somewhat reminiscent of the Sieve of Sundaram because it systematically produces multiples of primes in order to mark composites; it's better than Sundaram's in that each composite is crossed off exactly once (no overdraw). Likewise, each phi value is computed and assigned exactly once.
It is easier to understand the code if it is changed a little first:
enum { N = 3000000 };
vector<unsigned> primes;
vector<unsigned> phi(N + 1); // indexed directly with numbers, hence the +1
vector<bool> is_composite(N + 1); // indexed directly with numbers, hence the +1
phi[1] = 1; // phi[0] is 0 already; phi[1] needs to be set explicitly
for (unsigned i = 2; i <= N; ++i)
{
if (not is_composite[i]) // it's a prime
{
phi[i] = i - 1; // a prime is coprime to all numbers before it
primes.push_back(i);
}
for (auto current_prime: primes)
{
unsigned ith_multiple = i * current_prime;
if (ith_multiple > N)
break;
is_composite[ith_multiple] = true;
if (i % current_prime) // i and current_prime are coprime -> phi is multiplicative in this case
{
phi[ith_multiple] = phi[i] * (current_prime - 1); // (current_prime - 1) == phi[current_prime]
}
else
{
phi[ith_multiple] = phi[i] * current_prime; // based on phi(p^(k+1)) / phi(p^k) == p
break;
}
}
}
The computation of the values for phi(k) has to consider three different cases:
(1) k is prime: phi(k) = k - 1, which is trivial
(2) k = m * p with m and p coprime and p prime: phi(k) = phi(m * p) = phi(m) * phi(p) = phi(m) * (p - 1)
(3) k = m * p with m = m' * p^n and m' and p coprime and p prime: phi(k) = phi(m) * p
The two relevant mathematical facts are listed under Euler's product formula in the Wikipedia article on Euler's totient function. Case 1 is dealt with in the outer loop, case 2 is the then
branch of the condition in the inner loop, and case 3 is the else
branch which also terminates the inner loop. Cases 2 and 3 build phi values for composites out of preexisting phi values for smaller numbers, all of which ultimately derive from phi values for primes (set in the outer loop).
The brilliance of the algorithm lies in the way in which it arranges the work to be done, such that each value is computed exactly once and has already been computed when it is needed. The recursion it implements for composites is based on effectively splitting each composite by factoring out its smallest prime factor: m = m' * p. This facilitates the computation of the composite's phi as per cases 2 and 3 above, and it leads to a simple rule for producing composites without duplicates. Among other things, the outer loop presents all possible m' to the inner loop (although for i > N/2 the inner loop never takes any and the outer loop spins only for collecting the remaining primes). The inner loop then produces all composites that are the product of m' and a prime factor not exceeding the smallest of its own prime factors.
The code has been verified against a list of the first 100,000 phi values retrieved from the OEIS page for the phi function. It was written expressly to showcase the workings of the algorithm; before being used in a program it would have to be reviewed, tweaked, and hardened a bit (in particular to guard against overflow).
Addendum - In case anyone inadvertently skips DanaJ's comment: the is_composite[]
array is superfluous because the non-compositeness (primality) of a given i
can be ascertained by testing phi[i]
for zero in the outer loop. The reason is that phi values for composites are propagated up - they get computed during an earlier iteration when i
is one of their factors. Another way of reasoning is that is_composite[m]
is only ever set to true
when the corresponding phi value gets computed and stored, and those values can never be zero. Hence the test in the outer loop becomes if (phi[i] == 0)
. And implementers (as opposed to 'mere' connoisseurs) might want to look at DanaJ's comment even more closely...
来源:https://stackoverflow.com/questions/34260399/linear-time-eulers-totient-function-calculation