算法类——数学问题汇总

一、最大公约数和最小公倍数

最大公约数

正整数a与b的最大公约数是指a与b的所有公约数中最大的那个公约数。一般用gcd(a,b)来表示a和b的最大公约数。例如4和6的最大公约数是2,3和9的最大公约数是3。而求解最大公约数常用欧几里得算法(辗转相除法)。

定理:设a、b均为正整数,则gcd(a,b)=gcd(b,a%b)。

递归式代码:

int gcd(int a,int b)///递归调用
{
    if(b==0)
    {
        return a;
    }
    gcd(b,a%b);
}

函数调用:

#include<algorithm>///直接使用c++的内置函数
using namespace std;
__gcd(int a,int b)

最小公倍数

最小公倍数的求解是在最大公约数的基础上进行的。一般用lcm(a,b)来表示a和b的最小公倍数。

这里需要用到一个公式:lcm(a,b)*gcd(a,b)=a*b
所以 lcm(a,b) = a*b/gcd(a,b)
但在a*b计算时有可能造成溢出,因此更恰当的写法是lcm(a,b) = a/gcd(a,b)*b

二、素数

素数又称质数,是指除了1和本身之外,不能被其他数整除的一类数。即对给定的正整数n,如果对任意的正整数a(1<a<n),都有n%a!=0成立,那么称n是素数,否则,如果存在a(1<a<n),有n%=0成立,那么称n为合数。应特别值得注意的是,1既不是素数,也不是合数

素数的判断

注意到如果在2~n-1中存在n的约数,不妨设为k,即n%k==0,那么由k*(n/k)==n可知,n/k也是n的一个约数,且k与n/k中一定满足其中一个小于等于sqrt(n),另一个大于等于sqrt(n)。

bool isPrime(int n)
{
    if(n<=1)
    {
        return false;
    }
    int sqr = (int)sqrt( (double)m );
    for(i=2; i<=sqr; i++)
    {
        if(n%i==0)
            return false;

    }
    return true;
}

如果n没有接近int型变量的范围上界,有更简单的写法:

bool isPrime(int n)
{
    if(n<=1)
    {
        return false;
    }
    for(i=2; i*i<=n; i++)
    {
        if(n%i==0)
            return false;

    }
    return true;
}

筛法求素数

逐一判断1~n范围内的素数,总的时间复杂度很高,这里引入筛法求素数。

筛法的理论依据,任何一个数都能拆成多个素数的乘积(唯一分解定理),其思想是筛掉范围内所有的合数,而合数由素数乘积的到,素数的倍数就是合数,那么出现一个素数,我们就筛掉它的倍数。

素数筛法代码:

const int maxn =101;//表长
int prime[maxn];//存放所有素数
int pNum=0;//素数的个数
bool p[maxn]= {0}; //如果i是素数,p[i]为false,否则,p[i]为true 初始化为0
void Find_Prime()
{
    for(int i=2; i<maxn; i++)
    {
        if(p[i]==false) //如果i是素数
        {
            prime[pNum++]=i;//把素数i存放到prime数组中
            for(int j=i*2; j<maxn; j+=i)
            {
                //筛去所有i的倍数,循环条件不能写成j<=maxn
                p[j]=true;
            }
        }
    }
}

求解100以内所有素数代码:

#include<stdio.h>
const int maxn =101;//表长
int prime[maxn];//存放所有素数
int pNum=0;//素数的个数
bool p[maxn]= {0}; //如果i是素数,p[i]为false,否则,p[i]为true 初始化为0
void Find_Prime()
{
    for(int i=2; i<maxn; i++)
    {
        if(p[i]==false) //如果i是素数
        {
            prime[pNum++]=i;//把素数i存放到prime数组中
            for(int j=i*2; j<maxn; j+=i)
            {
                //筛去所有i的倍数,循环条件不能写成j<=maxn
                p[j]=true;
            }
        }
    }
}
int main()
{
    Find_Prime();
    for(int i=0; i<pNum; i++)
    {
        printf("%d ",prime[i]);
    }
    return 0;
}

三、质因子分解

所谓质因子分解是指将一个正整数 n 写成一个或多个质数的乘积形式,例如 24=2*2*2*3。显然,由于最后都要归结到若干不同质数的乘积,不妨先把素数表打印出来。

由于每个质因子都可以不止出现一次,因此不妨定义结构体 factor ,用来存放质因子及其个数,如下所示:

struct factor
{
    int x, cnt;    // x 为质因子,cnt 为其个数
} fac[10];

而有一个结论:对一个正整数 n 来说,如果它存在 [2,n] 范围内的质因子,要么这些质因子全部小于等于 sqrt(n) ,要么只存在一个大于 sqrt(n) 的质因子,而其余质因子全部小于等于 sqrt(n) 。这就给进行质因子分解提供了一个很好的思路:

  1. 枚举 1~sqrt(n) 范围内的所有质因子 p,判断 p 是否是 n 的因子。如果 p 是 n 的因子,那么给 fac 数组中增加质因子 p ,并初始化其个数为 0。然后只要 p 还是 n 的因子,就让 n 不断除以 p,每次操作令 p 的个数加 1,直到 p 不再是 n 的因子为止。如果p不是质因子,就直接跳过。

if(n % pri[i] == 0)      // prime[i] 是质因子
{
    fac[num].x = pri[i];//记录该质因子
    fac[num].cnt = 0;
    while(n%pri[i] == 0)   // 计算该质因子的个数
    {
        fac[num].cnt++;
        n /= pri[i];
    }
    num++;    // 不同质因子个数 +1
}

        2. 如果在上面步骤结束后 n 仍然大于 1,说明 n 有且仅有一个大于 sqrt(n) 的质因子(有可能是n本身),这时需要把这个质因子加入 fac 数组,并令其个数为 1。

if(n != 1)   // 存在大于 sqrt(n) 的质因子
{
    fac[num].x = n;
    fac[num++].cnt = 1;
}

至此,fac 数组中存放的就是质因子分解的结果,时间复杂度为O(根号n)。 

作者:王陸

-------------------------------------------

个性签名:罔谈彼短,靡持己长。做一个谦逊爱学的人!

本站使用「署名 4.0 国际」创作共享协议,转载请在文章明显位置注明作者及出处。鉴于博主处于考研复习期间,有什么问题请在评论区中提出,博主尽可能当天回复,加微信好友请注明原因

原文地址:https://www.cnblogs.com/wkfvawl/p/14543927.html