数学算法那些事

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

1、连续整数检测法.

此算法比较简单:

[php] view plain copy

print?

  1. /**
  2. * greatest common divisor
  3. *
  4. * @param int $a
  5. * @param int $b
  6. */
  7. function gcd($a, $b){
  8. $t = $a> $b ?$b :$a;
  9. while ($t>0){
  10. if($a%$t==0 && $b%$t ==0) break;
  11. --$t;
  12. }
  13. return $t;
  14. }

时间复杂度:最坏情况他们的最大公约数是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)的公约数是一样的,其最大公约数也必然相等,得证。

[php] view plain copy

print?

  1. /**
  2. * greatest common divisor
  3. * gcd(a, b) = gcd(b, a mod b)
  4. * @param int $a
  5. * @param int $b
  6. */
  7. function gcd($a, $b){
  8. $t = $a%$b;
  9. return $t > 0 ? gcd($b, $t): $b;
  10. }

欧几里德的时间复杂度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算法的实现如下:

[php] view plain copy

print?

  1. function gcd($a, $b){
  2. if ($a == 0) return $b;
  3. if ($b == 0)  return $a;
  4. if ($a % 2 == 0 && $b % 2 == 0)return 2 * gcd($a/2,$b/2);
  5. else if ($a % 2 == 0)  return gcd($a/2,$b);
  6. else if ($b % 2 == 0)  return gcd($a,$b/2);
  7. else  return gcd(($a + $b) / 2, ($a - $b) / 2);
  8. }

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,这两个参数仅用来输出结果。

[cpp] view plain copy

print?

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

3. 判断素数

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

算法实现:

[cpp] view plain copy

print?

  1. # include <math.h>
  2. int isPrime(int n)
  3. {
  4. int i ;
  5. for(i=2; i <= (int)sqrt((float)n); i++){
  6. if(n%i == 0 )break ;
  7. }
  8. if(i <= (int)sqrt((float)n))
  9. printf("%d is not a prime ! ", &n) ;
  10. else
  11. printf("%d is a prime ! ", &n) ;
  12. return 0 ;
  13. }
时间: 2024-11-10 06:14:32

数学算法那些事的相关文章

【转】Java学习---算法那些事

[更多参考] LeetCode算法 每日一题 1: Two Sum ----> 更多参考[今日头条--松鼠游学] 史上最全的五大算法总结 原文地址:https://www.cnblogs.com/ftl1012/p/9570688.html

子字符串查找之————关于KMP算法你不知道的事

写在前面: (阅读本文前需要了解KMP算法的基本思路.另外,本着大道至简的思想,本文的所有例子都会做从头到尾的讲解) 作者翻阅了大量网上现有的KMP算法博客,发现广为流传的竟然是一种不完整的KMP算法.即通过next数组来作为有限状态自动机,以此实现非匹配时的回退.这不失为一种好的方法. 但我们接下来要见识的是一种更好和更完整的方法————拥有完整DFA的KMP算法 先列出本文要介绍的方法与一般方法对比下的几大优点: 在最坏情况下,对字符串的操作次数仅为一般做法的三分之二. 在所有情况下,对字符

高中数学--算法与程序框图

①只有一个入口. ②只有一个出口. 注意: 一个菱形判断框有两个出口,而一个选择结构只有一个出口.不要将菱形框的出口和选择结构的出口混为一谈. ③结构内的每一部分都有机会被执行到.也就是说对每一个框来说都应当有一条从入口到出口的路径通过它. ④结构内不存在死循环,即无终止的循环. 程序框图的画法 (1)用自然语言表述算法: (2)确定每一个算法步骤所包含的逻辑结构,并用相应的程序框图表示,得到该步骤的程序框图: (3)将每一步得到的程序框图用流程线连接起来,并加上终端框,得到表示整个算法的程序框

BZOJ 2508 简单题 数学算法

题目大意:维护一个平面,支持三种操作: 0.加入一条直线(给的是两点式) 1.删除一条直线 2.询问到所有直线距离平方和最小的点 题解见 http://blog.sina.com.cn/s/blog_ab8386bc0101i1nj.html 我只是贴代码供参考的- - 注意我的abcdef和题解设的不一样- - 这简单题WA了两页- - #include <cmath> #include <cstdio> #include <cstring> #include <

BZOJ 2876 NOI2012 骑行川藏 二分+数学算法

题目大意:给定n段路,每段长度为si,如果在这段路上以vi的速度匀速行驶,那么消耗的体力为ki*(vi-v'i)^2*si,求在不超过体力上限情况下的最大速度 我去年买了个表- - 去网上百度了半天一元三次方程的求根公式才发现函数是递增的- - 百度百科写的什么NM破玩应- - 好像没讲明白- - MS只要知道拉格朗日乘数法就能差不多搞懂这道题了- - #include <cstdio> #include <cstring> #include <iostream> #i

BZOJ 2048 2009国家集训队 书堆 数学算法

题目大意:经典的物理上的桌边堆书问题,初中物理老师曾经还讲过,不过只记住了结论...没关系,简单证明一下就好 首先我们设由上至下第i本书比它下面那本书多伸出去的长度为a[i],前缀和为s[i],那么我们要求的就是s[n] 为了简化问题我们设一本书的长度为1 假设n=1 a[1]=1/2,毫无疑义 然后考虑两本书 两本书的时候,重心明显在距下面那本书左端点的3/4处,故a[2]=1-3/4=1/4 好的我知道了!第一本是1/2,第二本是1/4,那么第三本就是1/8! 这样想的同学都只过了样例. 我

【模板】一坨数学算法

求GCD 1 int gcd(int a,int b){return !b ? a : gcd(b,a%b);} 线性筛求[1,n]的质数 1 bool isprime[1000]; 2 int prime[100],tot; 3 void pri(int n) 4 { 5 tot = 0; 6 memset(isprime,true,sizeof(isprime)); 7 int i,j; 8 for(i=2;i<=n;i++) 9 { 10 if(isprime[i]) prime[++to

noip201403螺旋矩阵【普及组】数学算法

思路如下: 1.输入n>>a>>b; 2.用一个循环缩小范围求出a,b所示的数所在的圈数q; 3.再一个循环求出圈数q的第1个数的值sum; 4.用四个if判断a,b所示的数在本圈q的上或下或左或右; 5.根据位置求出t(在sum的基础上需要+的值) 6.输出<<sum+t; 代码如下: #include <cstdlib>#include <iostream> using namespace std; int main(int argc, ch

BZOJ 3000 Big Number 数学算法

题目大意:求n!在k进制下的位数 即 Stirling公式: 数据范围小就暴力,数据范围大套用Stirling公式 注意先利用log来避免数字过大而失精 最后答案要开long long #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double pi=