Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 1629 - 곱셈

sm_amoled 2022. 1. 4. 20:17

문제링크

https://www.acmicpc.net/problem/1629

문제

자연수 A를 B번 곱한 수를 알고 싶다. 단 구하려는 수가 매우 커질 수 있으므로 이를 C로 나눈 나머지를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.

출력

첫째 줄에 A를 B번 곱한 수를 C로 나눈 나머지를 출력한다.

조건

  • 시간 제한 : 0.5s
  • 메모리 제한 : 128MB

해설

굳이 A^B의 전체 수를 구한 다음 C로 나누어 나머지를 구하지 말고, 계속해서 나머지를 이용해 그 다음 나머지를 구해나가면 작은 메모리 안에서 해결이 가능하다.

A B C 가 10 11 12라고 하면 result는 A^B % C 와 같다. 

A   = (C x A') + K1
A^2 = ((C x A') + K1)^2 = C x ( ... ) + K1^2
→ 필요한 건 결국 나머지 값끼리의 곱 (다른 값들은 C의 배수)

A^P에서의 C로 나눈 나머지를 Kp 라고 한다면 
K11 → ((K5 * K5) * K1) % C
K5  → ((K2 * K2) * K1) % C
K2  → (K1 * K1) % C
K1  → A % C

 

 

 

위와 같은 방법으로 지수가 1일 때부터 필요한 값들을 만들면서 올라오면 필요한 나머지를 구할 수 있다. 여기에서 P가 홀수일 때는 3개의 나머지를 곱하게 되는데, 만약 C 값이 2^32에 가까워서, 곱해줄 나머지 값들도 2^32에 가깝다면 3개의 나머지를 곱했을 때 2^96에 가까운 값이 나오게 되므로 long long 자료형을 사용해도 정상적으로 값을 담을 수 없다. 따라서 우리가 필요한 값만 사용해주면 되므로, ((K^P’ * K^P’ % C) * K1) % C 와 같은 식을 사용하여 미리 나머지를 구해 long long 자료형의 범위인 2^64 범위 내에 담을 수 있게 해준다.

 

풀이

B에 대해 B/2일때의 값을 temp에 저장하여 B일때의 나머지를 반환해준다. 여기에서 B가 짝수라면 B/2일때의 값을 그대로 두 번 곱해 나머지를 구해주면 되고, B가 홀수라면 위에서 구한 나머지에 A를 한 번 더 곱해 나머지를 구해준다.

long long remains(int A, int B, int C) {
    if(B == 1) return A % C;
    
    long long temp = remains(A, B/2, C);
    
    if(B % 2 == 0) {
        return (temp*temp) % C;
    } else {
        return ((temp*temp) % C * A) % C;
    }
}

 

A, B, C를 입력받은 후 아래 코드를 실행시키면 원하는 결과를 얻을 수 있다.

cout << remains(A, B, C);

 


코멘트

생각보다 어려웠다. 이런 풀이방법도 분할 정복에 들어가는구나...

 


코드

#include <iostream>

using namespace std;

long long remains(int A, int B, int C) {
    if(B == 1) return A % C;
    
    long long temp = remains(A, B/2, C);
    
    if(B % 2 == 0) {
        return (temp*temp) % C;
    } else {
        return ((temp*temp) % C * A) % C;
    }
}

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    
    cout << remains(A, B, C);
        
    return 0;
}

문제 해결에 대한 고민

처음에는 B를 찾아갈 때 까지 나머지에 계속 A를 곱하면서 B의 카운트를 1씩 올리려고 했었다. 이 경우의 시간복잡도는 O(B). 근데 B가 2^32의 값을 가지면서 시간초과가 뜨더라. 그래서 생각해낸 방법이 위에처럼 반으로 접으면서 해결하는 방법이고, O(log B)의 시간복잡도를 갖는다. 이런 것도 분할정복에 들어가는군.

#include <iostream>

using namespace std;

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    
    int remain = 1;
    
    for(int i = 0; i < B; i++) {
        remain = remain * A % C;
    }
    
    cout << remain;
        
    return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 2836 - 수상 택시  (0) 2022.01.04
[백준] 3392 - 화성지도  (0) 2022.01.04
[백준] 2170 - 선 긋기  (0) 2022.01.04
[백준] 1934 - 최소공배수  (0) 2022.01.04
[백준] 1676 - 팩토리얼 0의 개수  (0) 2022.01.04