第四章 数学知识(一)(质数,质因子,)

一、数论

(一)判断质数

1、质数合数都是针对>=2的整数

2、质数的判断

一些因数的判断是不必要的。

#include<bits/stdc++.h>
//判断素数O(n^0.5)
using namespace std;
int n;
bool is_Prime(int n)
{
    if(n<2)return false;
    for(int i=2;i<n/i;i++)
    {
        if(n%i==0)return false;
    }
    return true;
}
int main()
{
    cin>>n;
    if(is_Prime(n))puts("Yes");
        else puts("No");
}

(二)分解质因数和求(0~N)内的所有质数

1、初等方法(朴素方法)

(1) 因为n最多只有一个大于sqrt(n)的因子。可以和质数判断一样,n/i为条件进行迭代。

最后如果n>1,剩下的就是最后的因子(大于sqrt(n)的数)。如果等于1,那么在前面的循环中已经找到所有的因子将其除掉了。

最好O(lg(n)):当n是某个质数的整数次方的时候

#include<bits/stdc++.h>
//求解质因数869
using namespace std;
int n;
void divide(int x)
{
    for(int i=2;i<=n/i;i++)
    {
        if(n%i==0)
        {
            int res=0;
            while(n%i==0)
            {
                n/=i;
                res++;
            }
            cout<<i<<" "<<res<<endl;
        }
    }
    if(n>1)
        cout<<n<<" "<<1<<endl;
}
int main()
{
   cin>>n;
   divide(n);
   return 0;
}

(2)分析时间复杂度

当进行n次循环的时候,i=2的时候需要n/2次循环,依此类推,得到时间复杂度为O(n ln n)<O(n log n)。

2、(埃氏筛法)优化方法:只需要把所有的质数的倍数筛掉

     按照之前的方法筛选的时候留下的都是质数,观察可以看到被筛掉的合数可以只通过前面的质数筛掉。根据质数原理:0~n之间的质数的个数为 n/ln(n) 。所以可以在之前的时间复杂度上除以一个ln(n),结果为O(n log( log n))

这样从小到大的筛法,将迭代到的所有的数的倍数都处理掉之后,每次遇到的还未被消掉的就是素数。

#include<bits/stdc++.h>
//筛选质数
using namespace std;
const int N=1e6+10;
int n,idx;
bool st[N];//是否被消除掉
int res[N];//保存质数
void divide(int x)
{
    for(int i=2;i<=n;i++)
        //前面的将其倍数全部删除之后,每次迭代到的st为false就是素数
    {
        if(!st[i])
        {
           res[idx++]=i;
           //消除掉所有的倍数。
           for(int j=i+i;j<=n;j+=i)st[j]=true;
        }
    }
}
int main()
{
   cin>>n;
   divide(n);
   for(int i=0;i<idx;i++)cout<<res[i]<<" ";
   return 0;
}

3、线性筛法(只会被自己的最小质因子筛掉))

是埃氏筛法(一个合数可能会被多个质数筛掉)的优化,

 

对于每一个数,从小到大遍历所有的质数,让每个数只被自己的最小质因数筛掉,只要找到第一个质数可以将某数删掉。

每一次循环先判断这个数有没有被筛掉过,没有被筛掉过,就保存当前数到数组。不管当前的i是不是素数,都让目前所有的素数从小到大依次乘以i。由于i是从1到2遍历。所以某个数总是被其最小质因子筛掉。

在埃氏筛法的基础上的优化位置是if(i%res[j]==0)break;

prime数组中的素数是递增的,当 i 能整除 prime[j],那么 i*prime[j+1] 这个合数肯定也可以被 prime[j] 筛掉,因为i中含有prime[j], prime[j] 比 prime[j+1] 小。

我们没有必要让prime[j+1]去筛掉prime[j+1]*i,因为由于……==0,在后面一定存在一个i,使得prime[j]*i将其删掉。后面所有的Prime[j+……] 都是这样。

#include<bits/stdc++.h>
//筛选质数(线性筛法)
using namespace std;
const int N=1e6+10;
int n,idx;
bool st[N];//是否被消除掉
int res[N];//保存质数
void divide(int x)
{
    for(int i=2;i<=n;i++)
    {
      if(!st[i])res[idx++]=i;
      //从小到大遍历所有质数
      for(int j=0;res[j]<=n/i;j++)//设置边界,避免超出n
      {
         st[res[j]*i]=true;//让当前所有的质数乘以同一个数的结果筛掉
         if(i%res[j]==0)break;//res[j]一定是i的最小质因子
         //也一定是res[j]*i的
      }
    }
}
int main()
{
   cin>>n;
   divide(n);
   for(int i=0;i<idx;i++)cout<<res[i]<<" ";
   return 0;
}

(三)求约数的值和约数的个数和所有约数的和

 1、求一个整数的所有约数

#include<bits/stdc++.h>
//868 式除法求约数
using namespace std;
const int N=1e6+10;

vector<int>get_divisor(int n)
{
   vector<int>ans;
   for(int i=1;i<n/i;i++)
   {
       if(n%i==0)
       {
           ans.push_back(i);
           if(i!=n/i)ans.push_back(n/i);
       }
   }
   sort(ans.begin(),ans.end());
   return ans;
}
int main()
{
    int x;
    cin>>x;
    auto res=get_divisor(x);
    for(auto x:res)cout<<x<<" ";
}

2、求约数的个数

求一组数的乘积的约数的个数:将每个数分解质因数,使用map存储;

#include<bits/stdc++.h>
//868 除法求约数个数

using namespace std;
typedef long long LL;
const int N=1e9+7;
int main()
{
    int n;
    LL ans=1;
    cin>>n;
    unordered_map<int,int>hash;
    while(n--)
    {
        int x;
        cin>>x;
        for(int i=2;i<=x-i;i++)
        {
           while(x%i==0)
           {
              hash[i]++;
              x/=i;
           }
        }
        if(x>1)hash[x]++;
    }
    for(auto p: hash)ans=ans*(p.second+1)%N;
    cout<<ans<<endl;
}

3、求所有约数的和 

同样用hash保存底数和对应的指数。while(b--)循环求取不同底数的等差数列的和。将所有的数相乘。

#include<bits/stdc++.h>
//求所有约数的和
using namespace std;
typedef long long LL;
const int N=1e9+7;
int main()
{
    int n;
    LL res=1;
    cin>>n;
    unordered_map<int,int>hash;
    while(n--)
    {
        int x;
        cin>>x;
        for(int i=2;i<=x-i;i++)
        {
           while(x%i==0)
           {
              hash[i]++;//同样用哈希表保存数
              x/=i;
           }
        }
        if(x>1)hash[x]++;
    }
    for(auto p: hash)
    {
        LL a=p.first,b=p.second;//a为底数,b为指数
        LL t=1;
        while(b--)t=(t*a+1)%N;
        res=res*t%N;
    }
    cout<<res<<endl;
}

(四)欧几里得算法

      (a,b)的所有公约数约数的集合,与(b,a mod b)的公约数的集合完全一致。

可知最大公约数也完全相同。

        根据这一等价代换的方法,可以一直进行这样的  代换,直到 b=0的时候,直接返回a即可。

#include<bits/stdc++.h>
//求一对数的最大公约数
using namespace std;
typedef long long LL;
const int N=1e9+7;
int gcd(int a,int b)
{
    return b ?gcd(b,a % b):a;
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
       int a,b;
       cin>>a>>b;
       int ans=gcd(a,b);
       cout<<ans<<endl;
    }
}

  • 26
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值