前言 

        众所周知,最大公因数(gcd)是C++程序中第二常见的函数(仅次于判素数)。正因此,一个简单的gcd也能被不同的OIER写出不同的“境界”。

        话不多说,直接开始!


第一境界:暴力枚举!

        如果你是一个连while循环都没写过的超级蒟蒻(天南星科植物魔芋属植物),那么你该怎么写判最大公因数程序?

        答案很简单:暴力枚举!

        我们可以用循环变量i直接从A,B中的小值直接反向枚举到1,如果能同时被A,B整除,直接输出i就行了。

        这样写虽然长(其实也不是很长),但也是最直白的思路了:

int gcd(int a,int b)
{
	for(int i=min(a,b);i>=1;i--)
		if(a%i==0&&b%i==0) return i;
}

         但这样写的时间复杂度是O(min(a,b)),因此一旦A,B都特别大的时候(比如说都是2的31次方),这种方法的运行速度将会比乌龟还慢,自然会时间超限(TLE)。


第二境界:辗转相除法

        很快,你就学会了while循环,同时也去百度上搜索了辗转相除法

        辗转相除法就是定义一个变量R来储存A模B的值,再将A的值设为B,B的值设为R。以此类推,直到B=0为止。然后直接输出A就行了。

        这样写虽然长了一点,但是时间复杂度获得了飞跃性的提升:

int gcd(int a,int b)
{
	while(b>0)
	{
		int r=a%b;
		a=b;b=r;
	}
	return a;
}

        作为一名初学者,会这段程序就够了(bushi)。 


第三境界:递归

       一个寒假过去了,曾经的那名蒟蒻终于学会了递归。

        你发现,用递归写最大公因数又快了很多。

        用递归写最大公因数,还是基于辗转相除法。这样写,当A模B等于0时,返回B;否则返回递归求解B和A模B的最大公因数的值。

        于是,你写出了一段递归求解的程序:

int gcd(int a,int b)
{
    if(a%b==0) return b;
    else return gcd(b,a%b);
}

        可这样写在OJ上没有通过。奇怪,百度上不是那么写的吗?

        事实上,根据辗转相除法的性质,应该是在B等于0是返回此时A的值,而不是在当A模B等于0时返回B的值。

        你把程序改了一下,果然通过(AC)了这道题目:

int gcd(int a,int b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}

第四境界:三元组

        你在《信息学奥赛一本通》中学到了有关三元组的知识。

        "啊?三元组?这似乎能运用到gcd上啊。"

        事实上,三元组在时间复杂度上与递归相比并没有变快。但是,使用三元组,原本两行的代码变成了一行。

        于是,求最大公因数的算法由两行变成了一行:

int gcd(int a,int b)
{return (b==0?a:gcd(b,a%b);}

第五境界:编译器自带函数!

        前段时间,你在一篇题解上发现了__gcd这个编译器自带函数。

        于是你一口气写出了判最大公因数的整个程序(真的只用了一口气的时间):

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a,b;
	cin>>a>>b;
	cout<<__gcd(a,b);
	return 0;
}

第六境界:从__gcd变回gcd

        但作为一名热爱共产党的新时代青少年,怎么能在gcd的前面加上两道杠呢?(你可以用键盘打出"gcd"试试)。

        难道是退回第四境界呢?当然不行。作为一名合格的OIER,写的算法当然要及时更新迭代,不可以落后。

        于是,你请出了#define语句(话说#define是真的好用),用它写出了“货真价实”的gcd:

#include<bits/stdc++.h>
#define gcd __gcd
using namespace std;
int main()
{
	int a,b;
	cin>>a>>b;
	cout<<gcd(a,b);
	return 0;
}

后记

在2023.10.19更改了之前程序的错误。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐