[백준] 2609번 - 최대공약수와 최소공배수

2025. 8. 27. 13:03·💻 개발/🧑🏻‍💻 코테
728x90
반응형
문제 링크 : https://www.acmicpc.net/problem/2609

 

 

문제 탐색하기

 

1) 문제 분석

  • 두 개의 수가 한 줄에 입력
  • 최대 공약수와 최소 공배수를 출력

 

 

코드 설계하기

1. 실행 구조

1) 입력값을 읽어서 공백을 기준으로 num1, num2으로 분리 

2) 두 수를 숫자로 변환 

3) 최대공약수, 최소공배수 함수 구현

4) 결과 출력

 

2. 고민이 되었던 부분

 

1) 최대공약수, 최소공배수에 대한 개념을 바로 잡았어야 했다. 

 

최대공약수(GCD)    

  • 두 수를 나누어 떨어지게 하는 "가장 큰 수"    
  • 예) 12, 18 → 공약수: 1, 2, 3, 6 → 최대공약수 = 6

최소공배수(LCM)    

  • 두 수 모두의 배수 중에서 "가장 작은 수"    
  • 예) 12, 18 → 공배수: 36, 72, 108 ... → 최소공배수 = 36

2) 기존 코드의 개선

(1) 최대 공약수 함수 : getGCD1

let getGCD1 = (num1, num2) => {
    if (num1 === 0) return num2;
    if (num2 === 0) return num1;

    let gcd = 1; // 공약수를 저장할 변수. 최소 1은 항상 공약수.

    // 두 수 중 작은 수까지만 확인
    for (let i = 2; i <= Math.min(num1, num2); i++) {
        // i가 num1, num2 모두를 나누어 떨어뜨릴 수 있다면
        if (num1 % i === 0 && num2 % i === 0) {
            gcd = i; // 공약수 갱신
        }
    }
    
    return gcd; // 마지막에 갱신된 값이 최대공약수
}

: 처음에 사용했던 방식은 '완전 탐색 방식'이었다. 두 수 중 더 작은 수까지 반복문을 돌면서 동시에 나누어 떨어지는 가장 큰 수를 찾는 방시식으로 구현했다. 이 방식의 구현은 직관적이고 이해하기 쉽지만, 두 수가 클 경우(예: 10^9) 성능이 떨어진다.

  • 시간복잡도 : (O(min(n1, n2)))

 

(2) 최소 공배수 함수 : getLCM1

let getLCM1 = (num1, num2) => {
    if (num1 === 0 || num2 === 0) return 0;
    let lcm = 1; // 후보값을 1부터 시작
    
    // 무한 반복 → 조건을 만족하면 break
    while (true) {
        // lcm이 num1, num2 모두로 나누어 떨어질 때 (즉, 공배수일 때)
        if ((lcm % num1 === 0) && (lcm % num2 === 0)) {
            break; // 처음 발견된 순간이 최소공배수
        }
        lcm++; // 아니라면 1씩 증가시키며 탐색
    }
    return lcm;
}

: 최소 공배수 또한, '탐색형' 방식이었다. 이 방식은 단순히 1부터 시작해서 차례대로 값을 올려가며 “두 수로 동시에 나눠지는 수”를 찾는다. 이 방식의 구현은 작은 수일 때는 잘 동작하지만, 큰 수일 경우 너무 오래 걸린다.

  • 시간복잡도 : O(num1 * num2)

 

3) 유클리드 호제법

: 유클리드 호제법(Euclidean Algorithm)은 두 수의 최대공약수(GCD)를 빠르게 구하는 방법이다.

 

  • 두 수 a, b (a > b)에 대해
    GCD(a, b) = GCD(b, a % b)
  • 나머지가 0이 될 때, 그때의 나누는 수가 바로 최대공약수

이 방법의 경우 시간복잡도가 O(log(min(a, b)))로서, 매우 빠르다.

 

또한, 최소 공배수의 경우, 두 수의 곱을 최대공약수로 나눈 값으로 볼 수 있기에, 최대공약수를 구하는 함수를 재사용하면 더욱 시간이 단축될 것이라 생각했다. 여기서 중요했던 점은 num1과 num2를 곱한 뒤에 최대공약수로 나누는 방식이 아닌, (num1 / gcd(num1, num2)) * num2 으로 순서를 바꾸어서, 오버플로우/정밀도 손실 완화하는 부분도 발견했다.

그 결과 시간이 무려 4분의 1로 줄어든 것을 발견할 수 있었다.

 

 

정답 코드
// 최대공약수(GCD) & 최소공배수(LCM) 구하기

// 입력 처리
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().trim().split("\n");

// 입력한 두 수
const [n1, n2] = input[0].split(" ").map(Number);

/*
    최대공약수(GCD)
    - 두 수를 나누어 떨어지게 하는 "가장 큰 수"
    - 예) 12, 18 → 공약수: 1, 2, 3, 6 → 최대공약수 = 6
*/

// 방법1 : 완전 탐색 방식
// - 두 수 중 더 작은 수까지 반복문을 돌면서 동시에 나누어 떨어지는 가장 큰 수를 찾는다.
// - 구현은 직관적이고 이해하기 쉽지만, 두 수가 클 경우(예: 10^9) 성능이 떨어진다.
// - 시간복잡도 : (O(min(n1, n2)))
let getGCD1 = (num1, num2) => {
    if (num1 === 0) return num2;
    if (num2 === 0) return num1;

    let gcd = 1; // 공약수를 저장할 변수. 최소 1은 항상 공약수.

    // 두 수 중 작은 수까지만 확인
    for (let i = 2; i <= Math.min(num1, num2); i++) {
        // i가 num1, num2 모두를 나누어 떨어뜨릴 수 있다면
        if (num1 % i === 0 && num2 % i === 0) {
            gcd = i; // 공약수 갱신
        }
    }
    
    return gcd; // 마지막에 갱신된 값이 최대공약수
}

// 방법2 : 유클리드 호제법(Euclidean Algorithm)
// - 원리: GCD(a, b) = GCD(b, a % b)
// - 즉, 두 수를 나눴을 때의 '나머지'를 이용해 점점 더 작은 문제로 줄여가다가, 나머지가 0이 되는 순간의 수가 최대공약수.
// - 시간복잡도: O(log(min(n1, n2))) → 매우 효율적.
let getGCD2 = (num1, num2) => {
    if (num1 === 0) return num2;
    if (num2 === 0) return num1;

    while (num2 !== 0) {
        let temp = num1 % num2; // 나머지를 구함
        num1 = num2;            // num2를 새로운 기준으로
        num2 = temp;            // 나머지를 다음 피제수로
    }
    return num1; // 나머지가 0일 때의 num1이 최대공약수
}

/*
    최소공배수(LCM)
    - 두 수 모두의 배수 중에서 "가장 작은 수"
    - 예) 12, 18 → 공배수: 36, 72, 108 ... → 최소공배수 = 36
*/

// 방법1 : 완전 탐색 방식
// - 1부터 시작해서 차례로 증가시키며, 두 수로 동시에 나눠지는 순간 멈춘다.
// - 구현이 직관적이지만, 최악의 경우 num1*num2까지 확인해야 해서 성능이 매우 떨어짐.
// - 시간복잡도 : O(num1 * num2) (비효율적)
let getLCM1 = (num1, num2) => {
    if (num1 === 0 || num2 === 0) return 0; // 0 포함 시 LCM은 0
    let lcm = 1; // 후보값을 1부터 시작
    
    // 무한 반복 → 조건을 만족하면 break
    while (true) {
        // lcm이 num1, num2 모두로 나누어 떨어질 때 (즉, 공배수일 때)
        if ((lcm % num1 === 0) && (lcm % num2 === 0)) {
            break; // 처음 발견된 순간이 최소공배수
        }
        lcm++; // 아니라면 1씩 증가시키며 탐색
    }
    return lcm;
}

// 방법2 : 최대공약수(GCD) 활용 공식
// - 성질: LCM(a,b) * GCD(a,b) = a * b
// - 따라서 LCM(a,b) = (a / GCD(a,b)) * b
// - 유클리드 호제법으로 GCD를 구한 후, 즉시 계산하므로 매우 효율적.
// - 시간복잡도 : O(log(min(n1, n2))) (실전용)
let getLCM2 = (num1, num2) => {
    if (num1 === 0 || num2 === 0) return 0; // 0 포함 시 LCM은 0
    
    const gcd = getGCD2(num1, num2);  // 최대공약수 구하기
    return (num1 / gcd) * num2;       // 곱하기 전에 나누어 overflow 방지
}
    

// 결과 출력
console.log(getGCD2(n1, n2)); // 최대공약수
console.log(getLCM2(n1, n2)); // 최소공배수

 

마무리하며

 

어제에 이어, 오늘도 백준 문제를 풀며 코딩테스트를 준비할 수 있어서 다행이라고 생각했다. 오늘 문제의 경우에는 어제보다 더 쉬운 문제에 속한다고 생각한다. 두 수를 입력 받고, 최대공약수와 최소공배수만 구해서 출력하면 됐기에 말이다. 하지만, 생각보다 쉬운 문제는 아니었다. 그냥 결과를 출력하는 것까지는 쉽겠지만, 이를 최적화하는 과정에 대해서 고민하는 것이 필요했고, 이 부분이 핵심이었다.

 

어제 문제를 풀고 난 뒤에는 AI나 search의 도움을 받지 않고 풀고자 결단했었지만, 오늘은 최대공약수와 최소공배수를 구하는 방법에 대해서 찾아보며 문제를 풀게 되면서 어제 내린 결단을 지키지 못한 순간도 있었다. 그래도 원리를 이해하고, 조금씩 조금씩 내 것으로 만드는 단계라고 생각한다. 꾸준히 풀다보면 점점 스스로의 힘을 키울 수 있지 않을까 싶기도 하다. 암튼 내일도 코테를 풀어보자!

728x90
반응형

'💻 개발 > 🧑🏻‍💻 코테' 카테고리의 다른 글

[백준] 1074번 - Z  (0) 2025.12.29
[백준] 1316번 - 그룹 단어 체크  (0) 2025.12.27
[백준] 2292번 - 벌집  (3) 2025.08.26
[백준] 1259번 - 팰린드롬수  (3) 2025.08.02
[백준] 30802번 - 웰컴키트  (4) 2025.07.30
'💻 개발/🧑🏻‍💻 코테' 카테고리의 다른 글
  • [백준] 1074번 - Z
  • [백준] 1316번 - 그룹 단어 체크
  • [백준] 2292번 - 벌집
  • [백준] 1259번 - 팰린드롬수
pangil_kim
pangil_kim
기록을 통해 지속적인 성장을 추구합니다.
  • pangil_kim
    멈추지 않는 기록
    pangil_kim
  • 전체
    오늘
    어제
  • 📝 글쓰기
      ⚙️ 관리

    • 분류 전체보기 (405) N
      • 💻 개발 (176) N
        • ※ 참고 지식 (9)
        • 🦕 React (13)
        • 🎩 Next.js (25)
        • 📘 TypeScript (4)
        • 📒 JavaScript (8)
        • 🟩 Node.js (7)
        • 📀 MySQL (24)
        • 🌸 Spring Boot (5)
        • 👷 SveleteKit (24)
        • 🩵 Flutter (11)
        • 🌀 Dart (2)
        • 🌈 CSS (5)
        • 🔸Git (1)
        • 🔥 Firebase (4)
        • 🧑🏻‍💻 코테 (29) N
        • 🕸️ 알고리즘 (4)
        • 🌤️ AWS (1) N
      • 📋 프로젝트 (4) N
        • ☄️ 트러블 슈팅 (2) N
        • 🧑🏻‍💻 서비스 소개 (2)
      • ✍🏻 회고 (52) N
        • ☀️ 취준일지 (6) N
        • 🍀 우테코 (32)
        • 👋 주간회고 (1) N
      • 📰 정보 공유 (12)
      • 🧑🏻‍💻 개발자라면? (1)
      • 🏫 한동대학교 (153)
        • Database (15)
        • Software Engineering (18)
        • EAP (22)
        • 일반화학 (26)
        • 25-1 수업 정리 (19)
        • Computer Networking (36)
        • OPIc (2)
        • 미술의 이해 (15)
  • 최근 글

  • 인기 글

  • 태그

    우아한테크코스
    FE
    typeScript
    CCM
    웹개발
    주일
    프리코스
    데이터베이스
    예배
    우테코 8기
    설교
    csee
    우테코
    찬양
    QT
    묵상
    어노인팅
    프론트엔드
    웹 프론트엔드 8기
    전산전자공학부
    한동대학교
    부트캠프
    네트워킹
    글로벌리더십학부
    날솟샘
    GLS
    computer networks and the internet
    고윤민교수님
    날마다 솟는 샘물
    컴네
  • 최근 댓글

  • 250x250
  • hELLO· Designed By정상우.v4.10.4
pangil_kim
[백준] 2609번 - 최대공약수와 최소공배수
상단으로

티스토리툴바