数论-计数问题

约数的个数

给出正整数n的唯一分解式n=p1^{a1}p2^{a2}p3^{a3}...pk^{ak},求n的正约数的个数。

不难看出,n的任意正约数也只能包含p1,p2,p3等素因子,而不能有新的素因子出现。对于n的某个素因子pi,它所求约数中的指数可以是0,1,2,...ai共ai+1种情况,而且不同的素因子之间相互独立。根据乘法原理,n的正约数个数为:

\prod (ai+1)=(a1+1)(a2+1)...(ak+1)

小于n且与n互素的整数个数。给出正整数n的唯一分解式 n=p1^{a1}p2^{a2}p3^{a3}...pk^{ak},求1,2,3,....n中与n互素的数的个数。

输入:正整数n。

输出:小于n且与n互素的整数个数。

运行结果:

用容斥原理。首先从总数n中分别减去是p1,p2,...pk的倍数的个数(对于素数p来说,“与p互素”和“不是p的倍数”等价),即n-\frac{n}{p1}-\frac{n}{p2}-...-\frac{n}{pk},然后加上“同时是两个素因子的倍数”的个数\frac{n}{p1p2} + \frac{n}{p1p3} + ... + \frac{n}{pk-1pk},再减去"同时是3个素因子的倍数" .写成一个学术味比较浓的公式就是 

\varphi (n)=\sum_{S\subseteq p1,p2,....,pk }^{}0 (-1)^{|S|}\frac{n}{\prod_{pi \in S}^{0}pi}

这里引入的新记号\varphi \left ( n \right )就是题目汇总所求的结果,称为欧拉函数。强烈建议初学者花一些时间理解这个公式。对于{p1,p2,...pk}的任意子集,“不与其中任何一个互素”的元素个数是\frac{n}{\prod_{pi\in S}^{0}pi},不过这一项的前面的是加号还是减号呢?这取决于S中的元素个数-奇数个就是减号,偶数个就是加号。

公式已得出,可计算起来很不方便。如果直接根据公式,需要计算多大2^k项的代数和,甚至可能比“暴力枚举”(依次判断1-n中每个数是否与n互素)还要慢。

下一步并不显然。上述公式可以变形成如下的形式

\varphi (n)=n(1-\frac{1}{p1})(1-\frac{1}{p2})...(1-\frac{1}{pk})

从而只需要O(k)的计算时间,在刚才的基础上大大提高了效率。为什么这个式子和上一个等价呢?直接考虑新公式的展开方式即可。展开式的每一项是从每个括号各选一个(选1或者-1/pi),全部乘起来以后再乘以n得到。这不正是最初的推导过程吗?

如果没有给出唯一分解式,需要用试除法依次判断\sqrt{n}内的素数是否是n的因子。这样,则需要先生成\sqrt{n}内的素数表,但其实并不用这么麻烦,只需要每次找到一个素因子之后把它除干净,即可保证找到的因子都是素数

int euler_phi(int n)
{
    int i, m, ans;

    ans = n;
    m = sqrt(n + 0.5);
    for(i = 2; i <= m; i++)
    {
        if(n % i == 0)
        {
            ans =  ans / i * (i-1);
            while(n % i == 0)
                n /= i;
        }
    }
    if(n > 1)
        ans = ans / n * (n-1);

    return ans;
}

1~n中所有数的欧拉phi函数值。并不需要依次计算,可以用与筛法求素数非常类似的方法,在O(nloglogn)时间内计算完毕。

原理:寻找素数,每个素数的倍数的phi值一定是包含一项 1-\frac{1}{i}值的,所以找出所有的倍数并且更新它的phi值。最后所有的素因子都找到了,也就得到了其所有倍数的phi值。

void phi_table(int n, int *phi)
{
    int i, j;
    for(i = 2; i <= n; i++)
        phi[i] = 0;
    phi[1] = 1;
    for(i = 2; i <= n; i++)
        if(!phi[i])  //保证pi是素数
            for(j = i; j <= n; j += i) //只要有i的素数因子, 就进行计算
            {
                if(!phi[j])
                    phi[j] = j;
                phi[j] = phi[j] / i * (i-1);
            }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值