zoukankan      html  css  js  c++  java
  • 数学算法那些事

    1. 三种方法求最大公约数

    1、连续整数检测法. 

    此算法比较简单:

    /**
     * greatest common divisor
     * 
     * @param int $a
     * @param int $b
     */
    function gcd($a, $b){ 
        $t = $a> $b ?$b :$a;
        while ($t>0){
            if($a%$t==0 && $b%$t ==0) break;
            --$t;
        }
        return $t;
    }

    时间复杂度:最坏情况他们的最大公约数是1,循环做了t-1次,最好情况是只做了1次,可以得出O(n)=n/2;


    2、欧几里德算法

    欧几里德算法又称辗转相除法, 用于计算两个整数a, b的最大公约数。其计算原理依赖于下面的定理:
    定理: gcd(a, b) = gcd(b, a mod b)
    证明:
         a可以表示成a = kb + r, 则r = a mod b
         假设d是a, b的一个公约数, 则有  d|a, d|b, 而r = a - kb, 因此d|r。
         因此,d是(b, a mod b)的公约数。
         加上d是(b,a mod b)的公约数,则d|b, d|r, 但是a = kb + r,因此d也是(a, b)的公约数。
         因此,(a, b) 和(a, a mod b)的公约数是一样的,其最大公约数也必然相等,得证。

    /**  
     * greatest common divisor  
     * gcd(a, b) = gcd(b, a mod b)  
     * @param int $a  
     * @param int $b  
     */  
    function gcd($a, $b){   
        $t = $a%$b;  
        return $t > 0 ? gcd($b, $t): $b;  
    }  

    欧几里德的时间复杂度O(n)= log n

    3、Stein 算法

    欧几里德算法是计算两个数最大公约数的传统算法,无论是理论,还是从效率上都是很好的。但是他有一个致命的缺陷,这个缺陷只有在很大的素数时才会显现出来。
    考虑现在的硬件平台,一般整数最多也就是64位, 对于这样的整数,计算两个数值就的模很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。
    Stein算法由J.Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。
    为了说明Stein算法的正确性,首先必须注意到以下结论:
      gcd(a, a) = a, 也就是一个数和他自己的公约数是其自身。
      gcd(ka, kb) = k * gcd(a, b),也就是最大公约数运算和倍乘运算可以交换,特殊的,当k=2时,说明两个偶数的最大公约数比如能被2整除。
    Stein算法的实现如下:

    function gcd($a, $b){ 
        if ($a == 0) return $b;
        if ($b == 0)  return $a;
        if ($a % 2 == 0 && $b % 2 == 0)return 2 * gcd($a/2,$b/2);
        else if ($a % 2 == 0)  return gcd($a/2,$b);
        else if ($b % 2 == 0)  return gcd($a,$b/2);
        else  return gcd(($a + $b) / 2, ($a - $b) / 2);
        
    }

    2. 排列组合

    从n中选m个数(0<m<=n) 问题可分解为:

    1. 首先从n个数中选取编号最大的数,然后在剩下的n-1个数里面选取m-1个数,直到从n-(m-1)个数中选取1个数为止。
    2. 从n个数中选取编号次小的一个数,继续执行1步,直到当前可选编号最大的数为m。
    很明显,上述方法是一个递归的过程,也就是说用递归的方法可以很干净利索地求得所有组合。

       求从数组a[1..n]中任选m个元素的所有组合。
       a[1..n]表示候选集,n为候选集大小,n>=m>0。
       b[1..M]用来存储当前组合中的元素(这里存储的是元素下标),
       常量M表示满足条件的一个组合中元素的个数,M=m,这两个参数仅用来输出结果。

    #include "stdafx.h" 
    void combine( int a[], int n, int m,  int b[], const int M ) {
     
      for(int i=n; i>=m; i--) {  // 注意这里的循环范围
        b[m-1] = i - 1;
    	if (m > 1) {
          combine(a,i-1,m-1,b,M);
    	} else  {                   // m == 1, 输出一个组合
          for(int j=M-1; j>=0; j--)
          cout << a[b[j]] << " ";
          cout << endl;
        }
      }
    }
    int main(){   
    	int a[] = {1,2,3,4,5};
        int b[3];
    	combine(a,  5, 3,  b, 3 );
    }

    3. 判断素数

    给定一个正整数n,用2到sqrt(n)之间的所有整数去除n,如果可以整除,则n不是素数,如果不可以整除,则n就是素数。这个算法的时间复杂度十分明了,时间复杂度是o(sqrt(n))。

    算法实现:

    # include <math.h>
    int isPrime(int n)
    {
        int i ;
        for(i=2; i <= (int)sqrt((float)n); i++){
            if(n%i == 0 )break ;
        }
        if(i <= (int)sqrt((float)n))
            printf("%d is not a prime ! ", &n) ;
        else
            printf("%d is a prime ! ", &n) ;
        return 0 ;
    }

    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    CSU 1333 Funny Car Racing
    FZU 2195 检查站点
    FZU 2193 So Hard
    ZOJ 1655 FZU 1125 Transport Goods
    zoj 2750 Idiomatic Phrases Game
    hdu 1874 畅通工程续
    hdu 2489 Minimal Ratio Tree
    hdu 3398 String
    洛谷 P2158 [SDOI2008]仪仗队 解题报告
    POJ 1958 Strange Towers of Hanoi 解题报告
  • 原文地址:https://www.cnblogs.com/iplus/p/4490155.html
Copyright © 2011-2022 走看看