基础算法整理----简单数学

1.GCD与LCM

    1.1欧几里得算法求最大公约数

               欧几里得算法即在计算机上实现的辗转相除法,多用于求取最大公约数(Greatest Common Divisor),简称GCD。一般用gcd(a,b)来表示 a 与 b 的最大公约数。根据欧几里得算法,可进行如下转换:gcd(a,b)=gcd(a,b%a),代码如下:

        在求取GCD时主要分为递归法与递推法,递归法代码简练,递推法则运算速度快。

[cpp]  view plain  copy

  1. /* 递归法 */  
  2. int gcd(int m, int n)  {  
  3.     return (m==0)?n:gcd(n%m, m);  
  4. }  
  5.   
  6. /* 迭代法(递推法)*/  
  7. int gcd(int m, int n)  
  8. {  
  9.     while(m>0){  
  10.         int c = n % m;  
  11.         n = m;  
  12.         m = c;  
  13.     }  
  14.     return n;  
  15. }   

1.2 LCM(最小公倍数)

       最小公倍数的求取是在求得GCD的基础上进一步求得的,假设a,b两个数,他们的最小公倍数为 a*b/gcd(a,b),则代码的书写极其简单:

int lcm(int m,int n){
    return m*n/gcd(m,n);
}


2.筛法求素数

        用筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛子为空时结束。

void _prime(){
    cnt = 1;
    memset(check,1,sizeof(check));//初始化认为所有数都为素数
    check[0] = check[1] = 0;//0和1不是素数
    for(long long i = 2; i <= MAX; i ++){
        if(check[i])
            prime[cnt++] = i;//保存素数i
        for(long long j = 1; j<cnt && prime[j]*i<MAX; j ++){
            check[prime[j]*i] = 0;//筛掉小于等于i的素数和i的积构成的合数
            if(i % prime[j] == 0)
            	break;
        }
    }
}

        上述代码中紫色部分最难理解,注释如下:

        首先,任何合数都可以表示为若干个素数的乘积,即 i 可表示为 prim[j]* 某个数,prime[j] 可看做其最小质因子。那么当i是 prime[j] 的整数倍,即 i%prime[j]==0 时,i*prime[j+1]肯定也会被筛走,因为 i*prime[j+1] 可看做 prime[j]*某个数*prime[j+1]。因此,当i%prime[j]时,可以终止筛选,没有必要继续进行下去。

3.康托展开 & 逆康托展开

       康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。这里只粘贴代码,详细请看:

https://blog.csdn.net/wbin233/article/details/72998375#%E7%AE%80%E8%BF%B0

//康托展开
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘
int cantor(int *a, int n){
    int x = 0;
    for (int i = 0; i < n; ++i) {
        int smaller = 0;  // 在当前位之后小于其的个数
        for (int j = i + 1; j < n; ++j) {
            if (a[j] < a[i])
                smaller++;
        }
        x += FAC[n - i - 1] * smaller; // 康托展开累加
    }
    return x;  // 康托展开值
}
//逆康托展开
static const int FAC[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};   // 阶乘

//康托展开逆运算
void decantor(int x, int n)
{
    vector<int> v;  // 存放当前可选数
    vector<int> a;  // 所求排列组合
    for(int i=1;i<=n;i++)
        v.push_back(i);
    for(int i=m;i>=1;i--)
    {
        int r = x % FAC[i-1];
        int t = x / FAC[i-1];
        x = r;
        sort(v.begin(),v.end());// 从小到大排序 
        a.push_back(v[t]);      // 剩余数里第t+1个数为当前位
        v.erase(v.begin()+t);   // 移除选做当前位的数
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值