(2013.03.08)求最大公约数_3种算法

来源:互联网 发布:nginx 日志记录真实ip 编辑:程序博客网 时间:2024/04/28 10:14

(2013.03.08) 求最大公约数_3种算法

一。实验题目

    求两个自然数m和n的最大公约数。(连续整数检测法,欧几里得算法,质因数分解法)

二。实验目的

    1.  复习数据结构课程的相关知识,实现课程间的平滑过渡;

    2. 掌握并应用算法的数学分析和后验分析方法;

    3. 理解这样一个观点:不同的算法能够解决相同的问题,这些算法的解题思路不同,复杂程度不同,解题效率也不同。


三。实验内容

1. 算法相关代码(伪代码与C++代码)

  1.1 连续整数检测法

 

1. t = min{m, n};2. m 除以 t,如果余数为0,则执行步骤3,否则,执行第4步;3. n除以t,如果余数为0,返回t的值作为结果,否则,执行第4步;4. t = t – 1, 转第2步。

 

// 连续整数检测  int CommonDivisor::continuousIntCheck(){int t = min();for (int i = t; i > 0; --i){if(0 == m % i && 0 == n % i){return i;}}return -1;}


 

 

  1. 2 欧几里得算法

1. r = m % n;2. 循环直到r = 0;  2.1 m = n;  2. 2 n = r;  2.3 r = m % n;3. 输出n.


 

// 欧几里得算法(辗转相除法)int CommonDivisor::euclideanAlgorithm(){int divisor = min();// 除数int dividend = max();// 被除数int mod = dividend % divisor;while(0 != mod){dividend = divisor;// 除数作为被除数divisor = mod;// 余数作为除数mod = dividend % divisor;}if(0 == mod){return divisor;}else{return -1;}}


 

 

  1.3 质因数分解法

0. 分解质因数1. 将m分解质因数;2. 将n分解质因数;3. 提取m和n中的公共质因数;4. 将m和n中的公共质因数相乘,乘积作为结果输出


 

// 分解质因数求最大公约数int CommonDivisor::breakPrimeFactor(){// 获取各质因数vector<int> nVector = getPrimeFactorSet(m);vector<int> mVector = getPrimeFactorSet(n);// 找出公共的质因数并相乘int result = 1;unsigned i1 = 0;unsigned i2 = 0;while(i1 < nVector.size() && i2 < mVector.size()){while(nVector[i1] != mVector[i2]){if(nVector[i1] < mVector[i2]){++i1;}if(i1 >= nVector.size()){break;}if(nVector[i1] > mVector[i2]){++i2;}if(i2 >= mVector.size()){break;}}// 越界判断if(i1 >= nVector.size() || i2 >= mVector.size()){break;}// nVector[i1] == mVector[i2])result *= nVector[i1];++i1;++i2;}return result;}


 

 

2. 完整代码:(包含语句执行次数检则方法及时间计算)

/** * 文件名:CommonDivisor.cpp * 程序功能:以三种方法求两数的最大公约数,并对三种方法进行算法复杂度计算。 * 编译环境:VS2005,Win7 * 程序结构:包含两个类: *            MyClock:用于记录程序运行所需时间,程序语句运行次数; *            CommonDivisor:包含两个数字及三种求最大公约数的方法。 *                           1. 连续整数检测;  *                           2. 欧几里得算法;  *                           3. 分解质因数方法。 * 作者名:Neicole * 编写时间:2013.03.05 * 联系方式:http://blog.csdn.net/neicole **/#include <iostream>#include <vector>#include <cstdlib>// rand()#include <math.h>// sqrt(double)#include <fstream>// 读写文件using namespace std;#define DEBUGMODE// 定义宏和全局变量以作算法效率测试#ifdef DEBUGMODE#include <ctime>// clock() 函数返回自程序开始运行的处理器时间class MyClock{private:clock_t startTime;// 记录开始时间clock_t endTime;// 记录结束时间long runNum;// 记录运行次数public:MyClock::MyClock(){clear();}inline int start(){startTime = clock();return startTime;}inline int end(){endTime = clock();return endTime;}inline int getRunTime(){return (endTime-startTime)/CLOCKS_PER_SEC*1000.0;}inline int add(){++runNum;return runNum;}inline int getRunNum(){return runNum;}void clear(){runNum = 0;startTime = 0;endTime = 0;}};#endif/****************************  最大公约数的类声明 **********************************/class CommonDivisor{private:int m;// 测试数据int n;public:#ifdef DEBUGMODEMyClock myClock;#endifpublic:explicit CommonDivisor(int);// 构造函数(指定范围内的随机数据)CommonDivisor(int, int);// 构造函数(指定数据)inline int getM();// 获取m的值inline int getN();// 获取n的值inline int min();// 求出最小值inline int max();// 求出最大值int continuousIntCheck();// 连续整数检测  int euclideanAlgorithm();// 欧几里得算法bool isPrime(int);  // 判断是否质数vector<int> getPrimeFactorSet(int);// 获取某个数的质因数集int breakPrimeFactor();// 分解质因数};/************************************ 类定义 **********************************/// 构造函数(随机数据)CommonDivisor::CommonDivisor(int maxTestValue){m = rand() % maxTestValue + 1; // 大于等于1,小于 maxTestValue 的随机数n = rand() % maxTestValue + 1;// 大于等于1,小于 MAX_TEST_VALUE 的随机数}// 构造函数(指定数据)CommonDivisor::CommonDivisor(int m, int n){this -> m = m;this -> n = n;}// 获取m的值int CommonDivisor::getM(){return m;}// 获取n的值int CommonDivisor::getN(){return n;}// 求出最小值int CommonDivisor::min(){return m < n ? m : n;}// 求出最小值int CommonDivisor::max(){return m > n ? m : n;}// 连续整数检测  int CommonDivisor::continuousIntCheck(){#ifdef DEBUGMODEmyClock.clear();myClock.start();myClock.add();#endifint t = min();for (int i = t; i > 0; --i){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifif(0 == m % i && 0 == n % i){#ifdef DEBUGMODEmyClock.end();#endifreturn i;}}#ifdef DEBUGMODEmyClock.end();#endifreturn -1;}// 欧几里得算法(辗转相除法)int CommonDivisor::euclideanAlgorithm(){#ifdef DEBUGMODEmyClock.clear();myClock.start();myClock.add();#endifint divisor = min();// 除数int dividend = max();// 被除数int mod = dividend % divisor;while(0 != mod){dividend = divisor;// 除数作为被除数divisor = mod;// 余数作为除数mod = dividend % divisor;#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endif}#ifdef DEBUGMODEmyClock.end();#endifif(0 == mod){return divisor;}else{return -1;}}// 判断是否质数bool CommonDivisor::isPrime(int source){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifif(2 == source || 3 == source){return true;}// 判断大于3的数int sqrtSou = (int)sqrt((double)source);for(int i = sqrtSou; i >= 2; --i){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifif(0 == source % i){return false;}}return true;}// 获取某个数的质因数集vector<int> CommonDivisor::getPrimeFactorSet(int source){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifint compareSize = source;vector<int> result;// 初始化使元素1于向量组中bool canDiv = true;// 可被整除while(canDiv){bool hasNotDiv = true;;for(int i = 2; hasNotDiv && i <= compareSize; ++i){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifif(isPrime(i) && 0 == source % i){result.push_back(i);source = source / i;hasNotDiv = false;}}if(hasNotDiv){// 终止条件,已找不出能被整除的数canDiv = false;}}return result;}// 分解质因数求最大公约数int CommonDivisor::breakPrimeFactor(){#ifdef DEBUGMODEmyClock.clear();myClock.start();myClock.add();#endif// 获取各质因数vector<int> nVector = getPrimeFactorSet(m);vector<int> mVector = getPrimeFactorSet(n);// 找出公共的质因数并相乘int result = 1;unsigned i1 = 0;unsigned i2 = 0;while(i1 < nVector.size() && i2 < mVector.size()){while(nVector[i1] != mVector[i2]){#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifif(nVector[i1] < mVector[i2]){++i1;}if(i1 >= nVector.size()){break;}if(nVector[i1] > mVector[i2]){++i2;}if(i2 >= mVector.size()){break;}}// 越界判断if(i1 >= nVector.size() || i2 >= mVector.size()){break;}// nVector[i1] == mVector[i2])#ifdef DEBUGMODEmyClock.add();// 运行次数加1,用以估计时间复杂性#endifresult *= nVector[i1];++i1;++i2;}#ifdef DEBUGMODEmyClock.end();#endifreturn result;}/************************************ 测试函数 **********************************/#ifdef DEBUGMODE// 验证三种算法的结果正确性int valueCheck(){int maxTestValue = 100;for(int i = 0; i < 30; ++i, maxTestValue += 100){CommonDivisor testObj(maxTestValue);// 产生两个随机数int val1 = testObj.euclideanAlgorithm();int val2 = testObj.continuousIntCheck();int val3 = testObj.breakPrimeFactor();cout << "第" << i+1 << "组: "  << "m: " << testObj.getM() << "\tn: " << testObj.getN() << "\tTestFunction:   " << val1 << "\t" <<  val2 << "\t" << val3 << endl;if(val1 != val2 || val1 != val3){cout << "请注意,算法结果有误!\n";return -1;}}cout << "算法结果无误!\n";return 0;}// 记录三种算法的运算时间,语句执行次数/** 数据记录顺序如下,每个数据间使用空格间开:    随机数最大值 最大公约数 m值 n值 连续整数法执行时间 欧几里得算法执行时间 分解质因数法执行时间连续整数法语句执行次数欧几里得算法语句执行次数分解质因数法语句执行次数**/int clockCheck(){int maxTestValue = 100;for(int i = 0; i < 100; ++i, maxTestValue += 30){CommonDivisor testObj(maxTestValue);// 产生两个随机数int valM = testObj.getM();int valN = testObj.getN();int resVal1 = testObj.continuousIntCheck();int resValRunTime1 = testObj.myClock.getRunTime();int resValRunNum1 = testObj.myClock.getRunNum();int resVal2 = testObj.euclideanAlgorithm();int resValRunTime2 = testObj.myClock.getRunTime();int resValRunNum2 = testObj.myClock.getRunNum();int resVal3 = testObj.breakPrimeFactor();int resValRunTime3 = testObj.myClock.getRunTime();int resValRunNum3 = testObj.myClock.getRunNum();cout << maxTestValue  << " " << resVal1 << " " << valM << " " << valN << " "  // 由于在我的电脑上面测试的时间间隔太短(为零),因此这里不做输出与测试//<< resValRunTime1 << " " << resValRunTime2 << " " << resValRunTime3 << " " << resValRunNum1 << " " << resValRunNum2 << " " << resValRunNum3 << endl;ofstream myTestResult("myTestResult.txt", ios_base::out | ios_base::app);myTestResult << maxTestValue  << " " << resVal1 << " " << valM << " " << valN << " "  // 由于在我的电脑上面测试的时间间隔太短(为零),因此这里不做输出与测试//<< resValRunTime1 << " " << resValRunTime2 << " " << resValRunTime3 << " " << resValRunNum1 << " " << resValRunNum2 << " " << resValRunNum3 << endl;myTestResult.close();}return 0;}#endif/************************************ 主函数 **********************************/int main(void){#ifdef DEBUGMODE// 功能测试函数调用valueCheck();cout << endl;system("pause");cout << endl;clockCheck();#endifsystem("pause"); return 0;}


 

3. 运行结果:
 

 

四。实验结果及简要分析


 


  上图为由本实验所示代码所生成的部分数据,将需要求解的数值的最大值设为100, 130, 160... 每次增加30,这次实验的最大值最大增长至3000,观察分别使用连续整数法,欧几里得算法,分解质因数法求两数的最大公约数的语句执行次数,下面先逐个算法结果绘图观察。


  如上图,可更清晰地看出连续整数法求最大公约数的语句执行规律,可以看出,整体而言,求最大公约数所产生随机数的最大值基本上是与语句执行次数成正比的,语句执行次数会随着随机数的最大值的增大而增大,产生随机数最大值为500以内的语句执行次数都不会超过500,而随着随机数的增大,在最大值增长至2600时,语句执行次数已上升至2000次以上,而在随机数值最大值为3000内的数使用连续整数法求最大公约数,语句执行次数未发现超过3000次。

  如上图,可更清晰地看出欧几里得算法的语句执行规律,可以看出,整体而言,求最大公约数所产生随机数的最大值基本上是与语句执行次数成正比的,语句执行次数会随着随机数的最大值的增大而增大,但增长幅度还是比较小的,产生随机数最大值为由500增长到3000间,语法执行次数会在12次内。

 
  如上图,可更清晰地看出分解质因数语句执行次数走向,可以看出,求最大公约数所产生随机数的最大值基本上是与语句执行次数成正比的,语句执行次数会随着随机数的最大值的增大而增大,增长幅度还是比较大的,而且语句执行次数相当的大,随机数最大值于2800附近时语句执行次数更是超过了150000次,产生随机数最大值为由3000的语法执行次数会在200000以下。

   上图为求最大公约数的算法执行次数走向比较图,横坐标为求最大公约数所随机数的最大值,纵坐标为使用某种算法时语句的执行次数。由图可知,分解质因数法求最大公约数远比连续整数法和分解质因数法的语句执行次数要多,分解质因数法和欧几里得算法语句执行次数相对要少。

       而结合以上所有分析图可知,使用欧几里得算法求最大公约数的语句执行数次是相对最少的,在随机数最大值为3000的测试数据中,欧几里得算法的语句执行次数的上限为12,连续整数检测算法上限达到3000,比欧几里得算法多出200倍以上,而分解质因数算法语法执行次数上限竟高达200000,比欧几里得算法多出10000倍以上。

五。参考书籍

  《算法设计与分析》 王红梅 编,清华大学出版社

原创粉丝点击