解题思路:


很多人都用辗转相除法来递归,但是我们上中学时用的更多的应该是短除法,或者叫倒除法,我们进行进制转换时也会用到这种方法

所以我想利用短除法写出代码来表示,也是给大家提供一种不同的思考方式

那么什么是短除法:

假设我们输入 m=12,n=18 ,初始化最大公约数 gcd=1

我们发现 2 是12和18的一个公因子

我们令 m /= 2 ,n /= 2 ,gcd *= 2 

现在 m == 6 ,  n == 9 ,  gcd == 2

又发现 3 是 6 和 9 的一个公因子

我们令 m /= 3 ,  n /= 3 ,  gcd *= 3

现在m == 2 ,  n == 3 ,  gcd == 6

我们找不到除 1 以外的 2 和 3 的公因子了

所以现在的 gcd == 6 就是我们要找的最大公因数

而最小公倍数 lcm = gcd * m * n = 6 * 2 * 3 = 36


图片:

20200421113208150.jpg


参考代码:

#include <stdio.h>
int main()
{
    int m, n;
    int j, k;
    int lcm, gcd = 1;
    scanf("%d %d", &m, &n); //输入m和n
    j = m, k = n;           //用j和k表示m和n,不破坏m与n的值
    if (j > k)
    {
        j = n, k = m; //确保j是较小的那个
    }
    for (int i = 2; i <= j; i++) //循环寻找从i到j的因子(注意j是可变的,而i会被重置)
    {
        if (j % i == 0 && k % i == 0) //判断i是否为j和k的公因子
        {
            j /= i;
            k /= i;   //j与k分别除以i
            gcd *= i; //gcd乘以i
            i = 1;    //将i重置为1 (循环末尾会i++)
        }
    }
    lcm = gcd * j * k;         //求出最小公倍数lcm
    printf("%d %d", gcd, lcm); //输出gcd与lcm
    return 0;
}


看了评论区的诸多意见,决定重写这份时隔一年的代码:

//短除法求公约公倍 
#include <stdio.h>

int main() {
	int m, n;
	scanf("%d %d", &m, &n); 
	int gcd = 1;
	for (int i = 2; i <= m && i <= n; i++) {
		while (m % i == 0 && n % i == 0) { 
			m /= i;
			n /= i;
			gcd *= i;
		}
	}
	printf("%d %d\n", gcd, m * n * gcd);
	return 0;
}


短除法可能便于理解,但效率其实并不理想,时间复杂度可以达到O(n)(当输入质数时)

而辗转相除法被推断出时间复杂度为 O(log(n))

所以还是建议使用辗转相除法,代码量少且运算速度快:

//辗转相除法求公约公倍 
#include <stdio.h>

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

int main() {
	int m, n;
	scanf("%d %d", &m, &n); 
	int ans = gcd(m, n);
	printf("%d %d\n", ans, m * n / ans);
	return 0;
}


点赞(1)
 

0.0分

208 人评分

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 90 条评论

圆圆chortle 3年前 回复TA
请问,为什么要控制j<k呢,谢谢
笔墨 3年前 回复TA
@骏 符合一次不一定啊,它的for每循环一次是找最小公因数,所以它要找到每次最小公因数的结果然后相乘才能得到最大公因数
uq_65489053701 3年前 回复TA
#include<stdio.h>
int main()
{
    int a,b,j,k,i,x;
    scanf ("%d%d",&a,&b);
    j=a,k=b;
    
       if(b<a) 
        j=b; k=a;
     
    for (i>0;i<j;i--)
      {
    if(j/i==0&&k/i==0)
    break;
      }
    printf("%d",i);
    x=(j*k)/i;
    printf("%d",x);
    return 0;
 }
大佬们,萌新,请问哪里有问题?谢谢!
3年前 回复TA
@骏 if 条件 符合一次 就可以出结果啊,重置 &#039;i&#039; 是为什么???”
3年前 回复TA
1、j 和 k 多余了吧,为什么要确保 m和n 要对最小的、最少的数取余? 多了也不碍事啊!2、for循环:i 为什么要重置?if 条件 符号一次就够了啊,重置用来干嘛?
3年前 回复TA
@小小饼 1、j 和 k 多余了吧,为什么要确保 m和n 要对最小的、最少的数取余? 多了也不碍事啊!2、for循环:i 为什么要重置?if 条件 符号一次就够了啊,重置用来干嘛?
小小饼 3年前 回复TA
#include <stdio.h>
int main()
{
	int a,b,c;
    while(a)
    {   
        scanf("%d%d",&a,&b);
        printf("%d\n",a*b);
        c=a%b;
        while(c)
        {
            a=b;
            b=c;
            c=a%b;
        }
        printf("%d\n",b);
    }
}
大佬,问下,我哪里错了
狗子 3年前 回复TA
发个函数的做法
#include <stdio.h>
int gcd(int n, int m);
int lcm(int n, int m);
int main() {
    int n, m;
    scanf("%d%d", &n, &m);  
    printf("%d\n", gcd(n, m));
    printf("%d\n", lcm(n, m));
    return 0;
}
int gcd(int n, int m) {
    if (m <= n) {
    return m == 0 ? n:gcd(m, n % m);
    } else {
        return gcd(m, n);
    }
}
int lcm(int n, int m) {
    return n * m / gcd(n,m);
}
狗子 3年前 回复TA
@怪兽兽 接龙,发一个函数的做法      #include <stdio.h> int gcd(int n, int m); int lcm(int n, int m); int main() {     int n, m;     scanf("%d%d", &n, &m);       printf("%d
", gcd(n, m));     printf("%d
", lcm(n, m));     return 0; } int gcd(int n, int m) {     if (m <= n) {     return m == 0 ? n:gcd(m, n % m);     } else {         return gcd(m, n);     } } int lcm(int n, int m) {     return n * m / gcd(n,m); }
Drgeek 3年前 回复TA
@Drgeek vs 2012编译器太严格了,所以会报错,dev c++运行成功的,我去查了一下,你看看:C语言中等号=表示赋值运算符,例如E1=E2,表示将E2的值存放到变量E1中,E1必须是可修改的左值,也就是变量。