C언어; 최대공약수 구하기 총정리 (4가지 방법)

     

     

    1. 최대공약수를 구하는 방법

    1) 약수를 이용하여 구하기

    2) 두 수의 차이를 이용하여 구하기

    3) 유클리드 호제법을 이용하여 구하기

    4) 소인수분해를 이용하여 구하기

    2. 방법 별 구현 코드 예시

    1-1) 약수를 이용한 방법

    int a, b, tmp;
    scanf("%d %d", &a, &b);
    
    for (int i = 2; i <= a && i <= b; i++)
    {
        if (a % i == 0 && b % i == 0) tmp = i;
        else tmp = 1;
    }
    printf("%d", tmp);

    두 수 중에서 작은 수 까지 반복문을 실행한다. 그 이유는 두 수의 공약수는 항상 두 수 중 작은 수의 약수 중에서 존재하기 때문이다. (ex. 18의 약수는 1,2,3,6,9,18 이고, 6의 약수는 1,2,3,6 이다. 이떄 18의 약수 중에서 9와 18은 6(18과 6 중 작은 수)의 약수에 없으므로 공약수가 될 수 없다.)

    두 수의 공약수로 그 수를 나누면 나누어 떨어진다. 이것으로 판별하여 tmp에 작은 값부터 차례대로 입력되므로 반복문이 끝나면 tmp는 최대공약수가 저장 될 것이다. 만약 두 수를 나누어 떨어지게하는 수가 없다면, 1을 tmp에 입력한다.

     

    2) 두 수의 차이를 이용한 방법

    위 에서 a와 b는 서로소이다.

    위를 통해서 A-B (두 수의 차)는 G(최대공약수)의 배수이다. 그러므로 A-B의 약수 중에서 G(최대공약수)가 있다.

    설명한대로 코드를 작성해보겠다.

    두 수 a와 b중 큰 값에서 작은수를 빼야한다. 이 동작을 두 수가 같아 질 때까지 반복하면 2개의 케이스가 발생한다. 첫번째는 두 수가 1이 아닌 최대공약수로 같을 때이고, 두번째는 두 수가 1로 같을 때이다.

    int a, b;
    scanf("%d %d", &a, &b);
    
    while (a != b)
    {
        if (a >= b) a -= b;
        else b -= a;
    }
    
    printf("%d\n", a);

     

    3) 유클리드 호제법을 이용한 방법

    두 자연수 A와 B(A ≥ B)일 때,

    A ÷ B=q ··· r (q≠0)에서 A=Bq+r (0≤ r 〈 B)이다.

    따라서, 두 수 A와 B의 최대공약수는 Bq+r와 B의 최대공약수와 동치이다.

    Bq는 B의 배수이고, r은 B의 배수가 아니다. 그러므로 A와 B의 최대 공약수는 B와 r의 최대공약수라고 할 수 있다.

    정리) A와 B의 최대공약수는 A를 B로 나눈 나머지가 r 일 때, B와 r의 최대공약수와 같음. (단, A ≥ B)

    아래 참고. (k는 임의의 몫.)

    b[0] b[1]
    
    b[0]/b[1]=k ··· b[2]
    b[1]/b[2]=k ··· b[3]
    b[2]/b[3]=k ··· b[4]
            ⋮
    b[n]/b[n+1]=k ... b[n+2]
    
    b[n+2]==0 -> GCD: b[n+1]
    b[n+2]==1 -> GCD: 1
    
    -------------------------
    
    b[0] b[1]
    
    b[2]=b[0]%b[1]
    b[3]=b[1]%b[2]
            ⋮
    b[n+2]=b[n]%b[n+1]
    
    b[n+2]==0 -> GCD: b[n+1]
    b[n+2]==1 -> GCD: 1

    아래 코드는 위의 구상을 토대로 보기 편하게 작성해 보았다.

    int a[5] = {};
    
    scanf("%d %d", &a[0], &a[1]);
    
    int i = 0;
    while (1)
    {
        a[i + 2] = a[i] % a[i + 1];
    
        if (a[i + 2] == 0)
        {
            printf("%d\n", a[i + 1]);
            break;
        }
        if (a[i + 2] == 1)
        {
            printf("%d\n", a[i + 2]);
            break;
        }
        i++;
    }

     

    4) 소인수분해를 이용한 방법

    72와 90을 소인수분해하면 다음과 같다.

    이때 최대공약수는 두 수의 공통인 소수를 모조리 선택하여 곱한 값이다.

    즉, 72와 90의 최대공약수는 2X3^2=18이다.

    int isPrime(int n)
    {
        if (n <= 1)
            return 0;
        for (int i = 2; i * i <= n; ++i)
        {
            if (n % i == 0)
                return 0;
        }
        return 1;
    }
    
    int primeFactorization(int n, int factors[])
    {
        int index = 0;
        for (int i = 2; i <= n; ++i)
        {
            while (n % i == 0 && isPrime(i))
            {
                factors[index++] = i;
                n /= i;
            }
        }
        return index;
    }
    
    int gcd(int a, int b)
    {
        int factors_a[100] = {0};
        int factors_b[100] = {0};
        int cnt_a = primeFactorization(a, factors_a);
        int cnt_b = primeFactorization(b, factors_b);
    
        int gcd = 1;
    
        int i = 0, j = 0;
        while (i < cnt_a && j < cnt_b)
        {
            if (factors_a[i] == factors_b[j])
            {
                gcd *= factors_a[i];
                i++;
                j++;
            }
            else if (factors_a[i] < factors_b[j])
                i++;
            else
                j++;
        }
    
        return gcd;
    }
    
    int main()
    {
        int a, b;
        scanf("%d %d", &a, &b);
    
        int result = gcd(a, b);
        printf("%d\n", result);
    
        return 0;
    }

    3. 구현한 코드별 실행 시간 비교

    2121212121와 1212121212의 최대공약수를 각 코드마다 5번 실행시 걸리는 시간의 평균

     

    1) 약수를 이용하여 구하기 ⇒ 평균 3800ms

    2) 두 수의 차이를 이용하여 구하기 ⇒ 평균 2.6ms

    3) 유클리드 호제법을 이용하여 구하기 ⇒ 평균 3ms

    4) 소인수분해를 이용하여 구하기 ⇒ 평균 67.4ms

     

    댓글