程序设计方法与优化第二次作业
实验报告
一、题目分析
本次题目是探讨求两个数的最大公约数的算法,并比较各种算法的在不同规模测试数据下的平均运行时间,以此评定各种算法的效率。
二、算法构造
流程图:
辗转相除法(函数嵌套调用):
辗转相除法(函数递归调用):
穷举法:
更相减损法:
Stein算法(函数非递归调用):
Stein算法(函数递归调用):
三、算法实现
Java:
package package1;
public class Example {
//辗转相除法(函数嵌套调用)
public static int divisorFirst (int a,int b) /*自定义函数求两数的最大公约数*/
{
int temp; /*定义整型变量*/
if(a<b) /*通过比较求出两个数中的最大值和最小值*/
{ temp=a;a=b;b=temp;} /*设置中间变量进行两数交换*/
while(b!=0) /*通过循环求两数的余数,直到余数为0*/
{
temp=a%b;
a=b; /*变量数值交换*/
b=temp;
}
return (a); /*返回最大公约数到调用函数处*/
}
//辗转相除法(函数递归调用)
public static int gcdFirst (int a,int b)
{ if(a%b==0)//递归调用自身知道满足条件并返回最大公约数到调用函数处
return b;
else
return gcdFirst(b,a%b);
}
//穷举法(利用数学定义)
public static int divisorSecond (int a,int b) /*自定义函数求两数的最大公约数*/
{
int temp; /*定义义整型变量*/
temp=(a>b)?b:a; /*采种条件运算表达式求出两个数中的最小值*/
while(temp>0)
{
if (a%temp==0&&b%temp==0) /*只要找到一个数能同时被a,b所整除,则中止循环*/
break;
temp--; /*如不满足if条件则变量自减,直到能被a,b所整除*/
}
return (temp); /*返回满足条件的数到主调函数处*/
}
// 更相减损法
public static int gcdSecond(int m,int n)
{
int i=0,temp,x=1;
while(m%2==0 && n%2==0) //判断m和n能被多少个2整除
{
m/=2;
n/=2;
i+=1;
}
if(m<n) //m保存大的值
{
temp=m;
m=n;
n=temp;
}
while(x!=0)//以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止
{
x=m-n;
m=(n>x)?n:x;
n=(n<x)?n:x;
if(n==(m-n))
break;
}
if(i==0) /*返回满足条件的数到主调函数处*/
return n;
else
return (int)Math.pow(2,i)*n;
}
public static void main(String[] args){
//辗转相除法(函数嵌套调用)的实例
int arr[][]={{4,98},{46,445},{545,8584},{6568,84566},{56566,845464},{765856,4845800},{1655615,96515655},{85656562,545665860},{685855646,465006544},{656665585,465465585}};
for (int j = 0; j < 10; j++)
{
if (arr[j][0] <= 0 || arr[j][1] <= 0)
{
System.out.println("data error!");
System.exit(1);
}
}
double beginTime=System.nanoTime();
for(int i=0;i<10;i++)
{
System.out.println(arr[i][0]+"和"+arr[i][1]+"的最大公约数是:"+divisorFirst(arr[i][0],arr[i][1]));
}
double endTime=System.nanoTime();
System.out.println("辗转相除法(函数嵌套调用)的总共运行时间为:"+(endTime-beginTime)/1000.0+"微秒");
//辗转相除法(函数递归调用)的实例
double beginTime1=System.nanoTime();
for(int i=0;i<10;i++)
{
System.out.println(arr[i][0]+"和"+arr[i][1]+"的最大公约数是:"+gcdFirst(arr[i][0],arr[i][1]));
}
double endTime1=System.nanoTime();
System.out.println("辗转相除法(函数递归调用)的总共运行时间为:"+(endTime1-beginTime1)/1000.0+"微秒");
//穷举法(利用数学定义)的实例
double beginTime2=System.nanoTime();
for(int i=0;i<10;i++)
{
System.out.println(arr[i][0]+"和"+arr[i][1]+"的最大公约数是:"+divisorSecond(arr[i][0],arr[i][1]));
}
double endTime2=System.nanoTime();
System.out.println("穷举法(利用数学定义)的总共运行时间为:"+(endTime2-beginTime2)/1000.0+"微秒");
// 更相减损法的实例
double beginTime3=System.nanoTime();
for(int i=0;i<10;i++)
{
System.out.println(arr[i][0]+"和"+arr[i][1]+"的最大公约数是:"+gcdSecond(arr[i][0],arr[i][1]));
}
double endTime3=System.nanoTime();
System.out.println("更相减损法的总共运行时间为:"+(endTime3-beginTime3)/1000.0+"微秒");
}
}
C++:
#include<iostream>
#include<ctime>
using namespace std;
//Stein算法(函数非递归调用)
int Stein(unsigned int x, unsigned int y)
/* return the greatest common divisor of x and y */
{
int factor = 0;
int temp;
if (x < y)//如果x<y,交换x,y的值
{
temp = x;
x = y;
y = temp;
}
if (0 == y)//如果y=0,结束程序运行
{
return 0;
}
while (x != y)//当x!=y,进行循环
{
if (x & 0x1)// 判断x与16进制数0x1进行按位与运算的表达式的值,不为0则进入语句中
{/* when x is odd */
if (y & 0x1)// 判断y与16进制数0x1进行按位与运算的表达式的值,不为0则进入语句中
{/* when x and y are both odd */
y = (x - y) >> 1;//将x-y并右移一位的值赋给y
x -= y;将x-y的值赋给x
}
else
{/* when x is odd and y is even */
y >>= 1;//将y右移一位的值赋给y
}
}
else
{/* when x is even */
if (y & 0x1) // 判断y与16进制数0x1进行按位与运算的表达式的值,不为0则进入语句中
{/* when x is even and y is odd */
x >>= 1; //将x右移一位的值赋给x
if (x < y)//如果x<y,交换xy的值
{
temp = x;
x = y;
y = temp;
}
}
else
{/* when x and y are both even */
x >>= 1; //将x右移一位的值赋给x
y >>= 1; //将y右移一位的值赋给y
++factor;//factor的值加一
}
}
}
return (x << factor);//返回x左移factor位的值
}
//Stein算法(函数递归调用)
int gcd(int u, int v)
{
if (u == 0) return v;//u如果等于0,返回v
if (v == 0) return u;//v如果等于0,返回u
// look for factors of 2
if (~u & 1) // u is even//判断v按位取反后与1进行按位与运算的表达式的值,不为0则进入if语句中
{
if (v & 1) // v is odd//判断v与1进行按位与运算的表达式的值,不为0则进入if语句中
return gcd(u >> 1, v);//返回函数,递归调用函数
else // both u and v are even
return gcd(u >> 1, v >> 1) << 1;//返回函数,递归调用函数
}
if (~v & 1) // u is odd, v is even判断v按位取反后与1进行按位与运算的表达式的值,不为0则返回函数,递归调用函数
return gcd(u, v >> 1);
// reduce larger argument
if (u > v)//如果u>v,则返回函数,递归调用函数
return gcd((u - v) >> 1, v);
return gcd((v - u) >> 1, u);
}
int main()
{
//Stein算法(函数非递归调用)的实例
int arr[10][2] = { { 4,98 },{ 46,445 },{ 545,8584 },{ 6568,84566 },{ 56566,845464 },{ 765856,4845800 },{ 1655615,96515655 },{ 85656562,545665860 },{ 685855646,465006544 },{ 656665585,465465585 } };
for (int i = 0; i < 10; i++)
{
if (arr[i][0] <= 0 || arr[i][1] <= 0)
{
cout << "data error!";
return 0;
}
}
clock_t start, finish;
double total_time;
start = clock();
for (int i = 0; i<10; i++)
{
cout << "The higest common divisor is:" << Stein(arr[i][0], arr[i][1]) << endl;
}
finish = clock();
total_time = (double)(finish - start)*1000.0;//CLOCKS_PER_SEC=1000
cout << "Stein算法(函数非递归调用)的总共运行时间为:" << total_time << "微秒" << endl;
//Stein算法(函数递归调用)的实例
clock_t start1, finish1;
double total_time1;
start1 = clock();
for (int i = 0; i<10; i++)
{
cout << "The higest common divisor is:" << gcd(arr[i][0], arr[i][1]) << endl;
}
finish1 = clock();
total_time1 = (double)(finish1 - start1)*1000.0;//CLOCKS_PER_SEC=1000
cout << "Stein算法(函数递归调用)的总共运行时间为:" << total_time1 << "微秒";
return 0;
}
四、调试、测试及运行结果
数据的规模:输入十组数据并进行测试
{ { 4,98 },{ 46,445 },{ 545,8584 },{ 6568,84566 },{ 56566,845464 },{ 765856,4845800 },{ 1655615,96515655 },{ 85656562,545665860 },{ 685855646,465006544 },{ 656665585,465465585 } }
测试结果:
辗转相除法(函数嵌套调用)的五次运行时间分别为:(单位为微秒)
880.822、715.898、404.103、502.975、818.052
平均运行时间为:
(880.822+715.898+404.103+502.975+818.052)/5=664.370微秒=0.664毫秒
辗转相除法(函数递归调用)的五次运行时间分别为:
421.744、196.513、192.001、315.488、647.386
平均运行时间为:
(421.744+196.513+192.001+315.488+647.386)/5=354.626微秒=0.355毫秒
穷举法的五次运行时间分别为:
3135457.808、3037537.688、3030507.935、3163589.124、3070465.727
平均运行时间为:
(3135457.808+3037537.688+3030507.935+3163589.124+3070465.727
)/5=3087511微秒=3.088秒
更相减损法的五次运行时间分别为:
418.462、412.718、408.616、432.001、346.256
平均运行时间为:(
418.462+412.718+408.616+432.001+346.256)/5=403.617微秒=0.404毫秒
Stein算法(函数非递归调用)的五次运行时间分别为:
10000、15000、18000、12000、13000
平均运行时间为:
(10000+15000+18000+12000+13000)/5=13600微秒=13.6毫秒
Stein算法(函数递归调用)的五次运行时间分别为:
11000、15000、14000、12000、28000
平均运行时间为:
(11000+15000+14000+12000+28000)/5=16000微秒=16毫秒
五、经验归纳
由第四步中的测试结果我们知道辗转相除法(函数嵌套调用)的平均运行时间为0.664毫秒, 辗转相除法(函数递归调用)的平均运行时间为0.355毫秒, 穷举法的平均运行时间为3.088秒, 更相减损法的平均运行时间为0.404毫秒,很容易看出辗转相除法(函数递归调用)最优,更相减损法次之,辗转相除法(函数嵌套调用)再次之,穷举法的效率最差,远远不及前三种算法。由于这四种是在Java环境下运行的,另外两种算法目前如何在Java下运行不是很懂,只能放在c++环境下运行。且Java和c++运行环境不同,这使得程序运行时间没有太大的可比性。在c++环境下的Stein算法:
Stein算法(函数非递归调用)的平均运行时间为13.6毫秒
Stein算法(函数递归调用)的平均运行时间为16毫秒
容易看出Stein算法(函数非递归调用)的效率要优于Stein算法(函数递归调用)。