质因数的相关算法

文章介绍了积性函数的定义,强调了其乘性和完全积性的概念,并探讨了如何用算术函数和试除法判断质数,包括欧拉筛法(线性筛)在求素数中的高效应用,以及时间复杂度分析。
摘要由CSDN通过智能技术生成

积性函数

  • 积性函数基本都可用线性筛(欧拉筛)求解。
    ① 定 义 在 所 有 正 整 数 上 的 函 数 称 为 算 术 函 数 ( 或 者 数 论 函 数 ) ② 如 果 算 术 函 数 f 对 任 意 两 个 互 素 的 正 整 数 p 和 q , 均 有 f ( p q ) = f ( p ) f ( q ) , 称 为 积 性 函 数 ( 乘 性 函 数 ) ③ 如 果 对 任 意 两 个 正 整 数 p 和 q 均 有 f ( p q ) = f ( p ) f ( q ) , 则 为 完 全 积 性 函 数 ④ 积 性 函 数 的 和 函 数 , 也 是 积 性 函 数 。 若 f 是 积 性 函 数 , 则 F ( n ) = Σ d ∣ n f ( d ) 也 为 积 性 函 数 。 d ∣ n 表 示 d 是 n 的 因 子 。 ①定义在所有正整数上的函数称为算术函数(或者数论函数)\\ ②如果算术函数f对任意两个互素的正整数p和q,均有f(pq)=f(p)f(q),称为积性函数(乘性函数)\\ ③如果对任意两个正整数p和q均有f(pq)=f(p)f(q),则为完全积性函数\\ ④积性函数的和函数,也是积性函数。若f是积性函数,则F(n) = Σ_{d|n}f(d)也为积性函数。d|n表示d是n的因子。\\ ()fpqf(pq)=f(p)f(q)pqf(pq)=f(p)f(q)fF(n)=Σdnf(d)dndn
    • 常见算法问题:
      (1)算f的第n项 f ( n ) f(n) f(n)
      (2)算f在1到n的所有项: f ( 1 ) , f ( 2 ) , . . . , f ( n ) f(1),f(2),...,f(n) f(1),f(2),...,f(n)
      (3)算f前n项的和 Σ i = 1 n f ( i ) Σ_{i=1}^{n}f(i) Σi=1nf(i),即前缀和

质因数

  • 试除法求判定质数:

    • 即通过暴力算法求质数

    • 题目来源:Acwing 866:试除法判定质数

    • 时间复杂度 O ( n ) O(\sqrt{n}) O(n )

    • 优化原理(见代码注释处):对于一个数x,若是合数,则必可以拆分为2个约数(一大一小),若在i <= sqrt(x) <--> i*i <= x范围内,找不到小约数,则表示该数x是质数。

    #include<iostream>
    #include<cmath>
    using namespace std;
    
    int a[110];
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        int n;
        cin>>n;
        for(int i = 0; i < n; i++)
        {
            cin>>a[i];
            int flag = 1;
            if(a[i] < 2)
            {
                cout<<"No"<<endl; //1不是素数
                continue;
            }
            for(int j = 2; j<= a[i]/j; j++) //优化处,优化前为:j<=a[i]
            {
                if(a[i] % j == 0) 
                {
                    flag = 0;
                    cout<<"No"<<endl;
                    break;
                }
            }
            if(flag)
                cout<<"Yes"<<endl;
        }   
    }
    
  • 试除法分解质因数

    • 将一个合数分解成质因数相乘的形式: n = p 1 α 1 p 2 α 2 . . . p k α k n = p_1^{α_1}p_2^{α_2}...p_k^{α_k} n=p1α1p2α2...pkαk
    • 分解质因数时,分解出的最后一个质因数可能大于 n \sqrt{n} n ,例如:10以内的质因数: 2 和 5 , 10 < 5 2和5,\sqrt{10} < 5 2510 <5 所以5是未被循环代码算入,需特判。
    • 题目来源:Acwing 867:试除法判定质数
    #include<iostream>
    #include<cmath>
    using namespace std;
    
    int a[110];
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
    
        int n;
        cin>>n;
    
        for(int i = 0; i < n; i++)
        {
            cin>>a[i];
            int num = a[i];
            for(int j = 2; j <= sqrt(a[i]); j++) //不可能超过一半的数相乘的。有也就顶多只有1位
            {
                if(num % j == 0) //no a[i], is num, 会变的。
                {
                    int s = 0;
                    while(num % j == 0) num /= j, s++;
                    cout<<j <<" "<<s<<endl;
                }
            }
            if(num > 1) cout<<num <<" "<<1<<endl;//特判大于sqrt(a[i])的质因数
            cout<<endl;
        }
        return 0;
    }
    
  • 朴素筛法(埃氏筛法)求素数

    • 时 间 复 杂 度 : O ( N l o g l o g N ) 时间复杂度:O(NloglogN) O(NloglogN)
    • 题目来源:Acwing 868:筛质数
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    const int N = 1e6+10;
    int prime[N];//记录质数
    bool vis[N]; //筛去合数
    //埃氏筛
    void E_sieve(int n)
    {
        int num = 0; //记录素数个数
        memset(vis, false, sizeof vis);
        for(int i = 2; i <= sqrt(n); i++) //合数k一定可以被小于都等于sqrt(k)内的一个素数整除,eg:25:5*5,3*8=24,约数定理。约数成对出现
        {
            //素数都是false,未被true掉
            if(vis[i]) continue; //合数
            for(int j = i*i; j <= n; j += i) //+=i,因为每次都是倍数。而不是+1+1+1.。。
                vis[j] = true; //i*i,优化,因为前面的合数已被小的素数筛掉。
        }
        for(int i = 2; i <= n; i++)  //从2开始。小于=n,n也可能是素数。1不是素数,所以从2开始。
            if(!vis[i])
                prime[num++] = vis[i];
        cout<<num;
    }
    int main()
    {
        int n;
        cin>>n;
        E_sieve(n);
    }
    
  • 欧拉筛法(线性筛)

    • 时间复杂度: O ( n ) O(n) O(n)
    • 欧拉筛法确保每个合数只被筛一遍,即只被其最小质因数筛掉。核心代码:if(i % prime[j] == 0) break;
    • 如图:确保只被最小质因数筛掉
    • 详细内容见:Vajackye的github–数论
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    const int N = 1e6+10;
    int prime[N];//记录质数
    bool vis[N]; //筛去合数
    
    //欧拉筛,线性筛
    void Euler_sieve(int n)
    {
        int num = 0; //记录素数个数
        memset(vis, false, sizeof vis);
        memset(prime, 0, sizeof prime);
        for(int i = 2; i <= n; i++) //从2开始筛出所有素数
        {
            //素数,开头为2
            if(!vis[i]) prime[num++] = i;
            //筛prime素数组内的所有素数,筛最近的,避免重复筛,2->8,3->×(12,应该被2筛,被最小质数筛出)
            for(int j = 0; j < num; j++)//依次利用每个素数
            {
                if(i * prime[j] > n) break; //筛出在n范围内的素数(超出题目范围略)
                vis[prime[j] * i] = true; //按照i的倍数,依次筛出后面的素数。
                if(i % prime[j] == 0) break; //i是该素数的合数,则退出,因为,下一个素数*i 一定是本素数的合数,不满足:最小被素数除
            }
        }
        cout<<num;
    }
    
    int main()
    {
        int n;
        cin>>n;
        //E_sieve(n);
        Euler_sieve(n);
    }
    
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值