Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++로 구현하는 Schönhage-Strassen 알고리즘: 두 숫자의 곱셈 프로그램

Schönhage-Strassen(숀하게-스트라센) 알고리즘은 두 개의 수를 곱하는 데 사용되는 알고리즘으로, 특히 아주 큰 정수에 대해 점근적으로 빠른 성능을 보이는 곱셈 방식입니다.

실제 환경에서 이 알고리즘은 2215 ~ 2217(십진수 약 10,000 ~ 40,000자리) 범위를 넘는 숫자에 대해 카라추바(Karatsuba), 툼-쿡(Toom-Cook)과 같은 기존 방법보다 우수한 성능을 발휘하기 시작합니다.

알고리즘

아래 의사 코드는 각 수의 자릿수를 구하고, 선형 컨볼루션(linear convolution)을 계산한 후, 자릿수 올림(carry)을 반영하여 최종 곱을 구하는 과정을 보여줍니다.

시작
    함수 noOfDigit(x)
    변수 n을 선언하고 n = 0으로 초기화
    while (x > 0)
        x = x / 10
        n 증가
    n 반환
종료

시작
    schonhageStrassenMultiplication 알고리즘:
    schonhageStrassenMultiplication(a, b, n, m)
    배열 linearConvolution[n + m - 1] 선언
    i = 0부터 (n + m - 1) - 1까지
        linearConvolution[i] = 0
        long p = a
    i = 0부터 m - 1까지
        a = p
        j = 0부터 n - 1까지
            linearConvolution[i + j] += (b mod 10) * (a mod 10)
            a /= 10
            b /= 10
    i = (n + m - 2)부터 0까지
        linearConvolution[i] 출력
        long product = 0
        nextCarry = 0, base = 1
    i = 0부터 (n + m - 1) - 1까지
        linearConvolution[i] += nextCarry
        product = product + (base * (linearConvolution[i] % 10))
        nextCarry = linearConvolution[i] / 10
        base *= 10
    두 수의 곱(product) 출력
종료

핵심 단계 요약

  • 1단계: noOfDigit 함수를 통해 입력된 두 수의 자릿수 n, m을 구합니다.
  • 2단계: 길이가 n + m − 1인 선형 컨볼루션 배열을 0으로 초기화합니다.
  • 3단계: 두 수의 각 자릿수를 서로 곱한 값을 인덱스별로 누적하여 선형 컨볼루션을 만듭니다.
  • 4단계: 컨볼루션 배열의 각 항목에 자릿수 올림(nextCarry)을 적용하고, 낮은 자릿수부터 차례대로 더해 최종 곱(product)을 완성합니다.

C++ 예제 코드

#include <iostream>
using namespace std;

// 수의 자릿수를 구하는 함수
int noOfDigit(long x) {
    int n = 0;
    while (x > 0) {
        x /= 10;
        n++;
    }
    return n;
}

// Schonhage-Strassen 곱셈 수행 함수
void schonhageStrassenMultiplication(long a, long b, int n, int m) {
    // 선형 컨볼루션 배열을 0으로 초기화
    int linearConvolution[n + m - 1];
    for (int i = 0; i < (n + m - 1); i++)
        linearConvolution[i] = 0;

    long p = a;

    // 각 자릿수 곱을 누적하여 선형 컨볼루션 계산
    for (int i = 0; i < m; i++) {
        a = p;
        for (int j = 0; j < n; j++) {
            linearConvolution[i + j] += (b % 10) * (a % 10);
            a /= 10;
        }
        b /= 10;
    }

    cout << "선형 컨볼루션: ( ";
    for (int i = (n + m - 2); i >= 0; i--) {
        cout << linearConvolution[i] << " ";
    }
    cout << ")";

    // 자릿수 올림을 처리하여 최종 곱 계산
    long product = 0;
    int nextCarry = 0, base = 1;
    for (int i = 0; i < n + m - 1; i++) {
        linearConvolution[i] += nextCarry;
        product = product + (base * (linearConvolution[i] % 10));
        nextCarry = linearConvolution[i] / 10;
        base *= 10;
    }
    cout << "\n두 수의 곱: " << product;
}

int main(int argc, char **argv) {
    cout << "숫자를 입력하세요: ";
    long a, b;
    cin >> a >> b;
    int n = noOfDigit(a);
    int m = noOfDigit(b);
    schonhageStrassenMultiplication(a, b, n, m);
}

참고: 위 코드의 int linearConvolution[n + m - 1]처럼 크기를 변수로 지정하는 가변 길이 배열(VLA)은 표준 C++ 사양이 아니므로, 이식성을 높이려면 std::vector<int> 사용을 권장합니다.

실행 결과

숫자를 입력하세요: 1234 5679
선형 컨볼루션: ( 5 16 34 61 63 55 36 )
두 수의 곱: 7007886

위 실행 결과에서 선형 컨볼루션 값에 자릿수 올림을 차례로 적용하면 1234 × 5679 = 7007886이라는 최종 곱이 도출됩니다. Schönhage-Strassen 알고리즘의 시간 복잡도는 O(n log n · log log n)으로, 다루는 숫자의 자릿수가 많아질수록 학교식 곱셈(O(n²))이나 카라추바 알고리즘보다 훨씬 유리합니다.