该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
#include
#include
#include
int *prime, *v;
int q = 1, p = 1;
int pi(int n, int *prime, int len)
// 素数个数估计(用于n较小时)
{
int i = 0, mark = 0;
for (i = len - 1; i > 0; i--)
{
if (prime[i] < n)
{
mark = 1;
break;
}
}
if (mark)
return i + 1;
return 0;
}
int phi(int x, int a, int m) // 欧拉函数
{
if (a == m)
return (x / q) * p + v[x % q];
if (x < prime[a - 1])
return 1;
return phi(x, a - 1, m) - phi(x / prime[a - 1], a - 1, m);
}
int compute(int n)
{
char *mark;
int mark_len, count = 0, i, j, m = 7, sum = 0, s = 0, len, len2, len3;
mark_len = (n < 10000) ? 10002 : ((int)exp(2.0 / 3 * log(n)) + 1);
mark = (char *)malloc(sizeof(char) * mark_len);
memset(mark, 0, sizeof(char) * mark_len);
// 10000以内的素数用筛法打表
for (i = 2; i < (int)sqrt(mark_len); i++)
{
if (mark[i])
continue;
for (j = i + i; j < mark_len; j += i)
mark[j] = 1;
}
mark[0] = mark[1] = 1;
for (i = 0; i < mark_len; i++)
if (!mark[i])
count++;
prime = (int *)malloc(sizeof(int) * count);
j = 0;
// 把打表后的素数存到prime数组中
for (i = 0; i < mark_len; i++)
if (!mark[i])
prime[j++] = i;
if (n < 10000)
return pi(n, prime, count);
len = pi((int)exp(1.0 / 3 * log(n)), prime, count);
len2 = pi((int)sqrt(n), prime, count);
len3 = pi(mark_len - 1, prime, count);
j = mark_len - 2;
for (i = (int)exp(1.0 / 3 * log(n)); i <= (int)sqrt(n); i++)
{
if (!mark[i])
{
while (i * j > n)
{
if (!mark[j])
s++;
j--;
}
sum += s;
}
}
free(mark);
sum = (len2 - len) * len3 - sum;
sum += (len * (len - 1) - len2 * (len2 - 1)) / 2;
if (m > len)
m = len;
for (i = 0; i < m; i++)
{
q *= prime[i];
p *= prime[i] - 1;
}
v = (int *)malloc(sizeof(int) * q);
for (i = 0; i < q; i++)
v[i] = i;
for (i = 0; i < m; i++)
for (j = q - 1; j >= 0; j--)
v[j] -= v[j / prime[i]];
sum = phi(n, len, m) - sum + len - 1;
free(prime);
free(v);
return sum;
}
main()
{
int n;
while (~scanf("%d", &n))
{
q = 1, p = 1;
if (n == 0)
break;
else
printf("%d\n", compute(n));
}
return 0;
}
这是数学方法