카라츠바
Β - Last update: 2023-11-22

큰 자리수의 κ³±μ…ˆ μ•Œκ³ λ¦¬μ¦˜μΈ 카라츠바 μ•Œκ³ λ¦¬μ¦˜μ— λŒ€ν•΄μ„œ ν•΅μ‹¬λ§Œ 정리해본닀.

μ•½κ°„μ˜ μˆ˜ν•™

κ΅¬ν•˜κ³ μž ν•˜λŠ” κ³±μ…ˆμ΄ xβˆ—yx * y 인 경우λ₯Ό μƒκ°ν•˜μž. μ—¬κΈ°μ„œ xx, yyλŠ” μžμ—°μˆ˜μ΄λ‹€. νŽΈμ˜μƒ 두 수의 μžλ¦¬μˆ˜κ°€ κ°™λ‹€κ³  ν•˜μž. (λ§Œμ•½ 같지 μ•Šμ€ 경우라면, μž‘μ€ 녀석을 큰 λ…€μ„μ˜ 크기둜 λ§žμΆ”κ³  padding을 μΆ”κ°€ν•˜λ©΄ λœλ‹€) 그러면, xx, yyλŠ” μ•„λž˜μ²˜λŸΌ λ‹€μ‹œ μ“Έ 수 μžˆλ‹€.

  • x=x1Bm+x0x = x_1B^m + x_0
  • y=y1Bm+y0y = y_1B^m + y_0

μˆ˜μ‹μœΌλ‘œ 보면 λ³΅μž‘ν•΄ λ³΄μ΄λŠ”λ°, μ΄λŠ” λ‹¨μˆœνžˆ 123456=123βˆ—103+456123456 = 123 * 10^3 + 456 처럼 νŠΉμ • 수λ₯Ό high, low둜 λ‚˜λˆˆ 것 뿐이닀. high에 ν•΄λ‹Ήν•˜λŠ” 것이 x1x_1, y1y_1이고, low에 ν•΄λ‹Ήν•˜λŠ” 것이 x0x_0, y0y_0 이닀. 이λ₯Ό formalν•˜κ²Œ λ‚˜νƒ€λ‚Έ 것이 μœ„ μˆ˜μ‹μ΄κ³ , BBλŠ” 진법을 λ‚˜νƒ€λ‚΄κ³ , mm은 큰 수의 총 μžλ¦Ώμˆ˜κ°€ ll이라고 ν•  λ•Œ 절반으둜 λ‚˜λˆˆ 값이닀. (m=l/2m = l / 2, 절반으둜 λ‚˜λˆ„λ©΄ 쒋지 μ•Šκ² λŠ”κ°€?) 그러면 κ΅¬ν•˜κ³ μž ν–ˆλ˜ κ³±μ…ˆμ€ μ•„λž˜μ²˜λŸΌ λ‹€μ‹œ μ“Έ 수 μžˆλ‹€.

이것을 μ΄λŒ€λ‘œ κ³„μ‚°ν•˜λ©΄, κ³±μ…ˆμ˜ ꡬ간을 절반으둜 쀄인 λŒ€μ‹ , κ³±μ…ˆμ΄ νšŸμˆ˜κ°€ 4번으둜 λŠ˜μ–΄λ‚¬κΈ° λ•Œλ¬Έμ— μ‹œκ°„λ³΅μž‘λ„ 계산을 해보면 O(N2)O(N^2)이 되기 λ•Œλ¬Έμ— Naiveν•˜κ²Œ κ΅¬ν–ˆλ˜ κ²½μš°λž‘ λ‹€λ₯Όκ²Œ μ—†λ‹€. 그런데, λ§Œμ•½μ— μœ„ μˆ˜μ‹μ„ κ³„μ‚°ν•˜λŠ”λ°μ— κ³±μ…ˆμ˜ νšŸμˆ˜κ°€ 3번만 ν•„μš”ν•˜λ‹€λ©΄ μ–΄λ–¨κΉŒ? μžλ¦¬μˆ˜κ°€ μž‘μ€ κ²½μš°μ—λŠ” μœ„ 방식이 μƒμˆ˜ν•­μ„ 늘리기 λ•Œλ¬Έμ— 크게 μ˜λ―Έκ°€ μ—†μ§€λ§Œ, μžλ¦¬μˆ˜κ°€ 클 경우(NN이 클 경우)μ—λŠ” μΆ©λΆ„νžˆ μ˜λ―Έκ°€ μžˆλ‹€. 카라츠바 μ•Œκ³ λ¦¬μ¦˜μ˜ 핡심은 BmB^m의 κ³„μˆ˜μ— λΆ™μ–΄μžˆλŠ” 녀석을 μ•„λž˜μ²˜λŸΌ λ³€ν˜•ν•˜λŠ”λ°μ— μžˆλ‹€.

  • x1y0+x0y1=(x1+x0)(y1+y0)βˆ’x1y1βˆ’x0y0x_1y_0 + x_0y_1 = (x_1 + x_0)(y_1 + y_0) - x_1y_1 - x_0y_0

κ³±μ…ˆμ„ μ€„μΈλ‹€λ”λ‹ˆ λ‘λ²ˆμ—μ„œ 였히렀 μ„Έλ²ˆμœΌλ‘œ 늘리고 μžˆλ„€ 라고 ν•  수 μžˆλ‹€. 그런데 식을 μžμ„Ένžˆ 보자. x1y1x_1y_1κ³Ό x0y0x_0y_0은 μ–΄μ°¨ν”Ό 계산해야할 κ°’μ΄μ—ˆλ‹€. 즉, μΆ”κ°€λ‘œ 계산해야할 κ³±μ…ˆμ€ (x1+x0)(y1+y0)(x_1 + x_0)(y_1 + y_0) ν•˜λ‚˜λ‹€.

μ‹€μ œ κ΅¬ν˜„

μœ„ μ•Œκ³ λ¦¬μ¦˜μ€ κ°„λ‹¨ν•˜κ²Œ μž¬κ·€λ‘œ μ•„λž˜μ²˜λŸΌ κ΅¬ν˜„μ΄ κ°€λŠ₯ν•˜λ‹€. BmB^m 뢀뢄은 νŠΉμ • 자리수 뢀뢄에닀가 λ”ν•˜λŠ” κ²ƒμ΄λ―€λ‘œ, 배열에 잘 μ ‘κ·Όν•˜λŠ” κ²ƒμœΌλ‘œ μ²˜λ¦¬κ°€ κ°€λŠ₯ν•˜λ‹€. λ˜ν•œ, μΉ΄λΌμΈ λ°”λŠ” μƒμˆ˜ν•­μ΄ 큰 μ•Œκ³ λ¦¬μ¦˜μ΄κΈ° λ•Œλ¬Έμ—, μ‹€μ œλ‘œ μ¨λ¨Ήμ„λ§Œν•œ κ΅¬ν˜„μ€ naiveν•œ κ³±μ…ˆ κ΅¬ν˜„μ„ μ„žμ–΄μ„œ ν•œλ‹€.

// naive ν•œ κ³±μ…ˆ κ΅¬ν˜„, μ‹œκ°„λ³΅μž‘λ„ O(N^2)
void naive_mult(ull r[], ull x[], ull y[], int n) {
for(int i = 0; i < n; ++i) {
ull cur = x[i];
ull* rp = &r[i]; ull* yp = &y[0];
for(int j = 0; j < n; ++j, ++rp, ++yp) {
*rp += *yp * cur;
}
}
}
void karatsuba(ull r[], ull x[], ull y[], int n) {
if (n < 50) {
naive_mult(r, x, y, n); return; // 일정 자리수 μ΄ν•˜λ‘œ λ‚΄λ €κ°€λ©΄ karatsubaλŠ” λΉ„νš¨μœ¨μ μ΄λ‹€. naiveν•œ κ³±μ…ˆμ„ μ“°μž
}
int m = n >> 1; // NλŠ” κ°€λŠ₯ν•œν•œ N / 2 λ”± λ–¨μ–΄μ§€κ²Œλ˜λŠ” κ°€μž₯ 쒋은 수λ₯Ό μ„ νƒν•˜μž.
ull xs[SZ], ys[SZ], tmp[SZ];
for(int i = 0; i < m; ++i) xs[i] = x[i] + x[i + m], ys[i] = y[i] + y[i + m];
for(int i = 0; i < n; ++i) tmp[i] = 0;
// 4개의 κ³±μ…ˆ -> 3개의 κ³±μ…ˆμœΌλ‘œ 쀄어듬
karatsuba(tmp, xs, ys, m);
karatsuba(r, x, y, m);
karatsuba(r + n, x + m, y + m, m); // B^2m κ΅¬κ°„μ˜ κ²°κ³ΌλŠ” μ—¬κΈ°λ‘œ λ“€μ–΄κ°„λ‹€.
for(int i = 0; i < n; ++i) tmp[i] -= r[i] + r[i + n]; // (x0 + x1)(y0 + y1) - x0y0 - x1y1
for(int i = 0; i < n; ++i) r[i + m] += tmp[i]; // * B^m
}

Time Complexity

이 μ•Œκ³ λ¦¬μ¦˜μ˜ κ΅¬ν˜„μ„ μ‚΄νŽ΄λ³΄λ©΄, 뢄할정볡을 ν•˜λŠ” 경우의 μ‹œκ°„λ³΅μž‘λ„ 계산을 μ΄μš©ν•˜μ—¬ μ‹œκ°„λ³΅μž‘λ„λ₯Ό ꡬ할 수 μžˆλ‹€. κ·ΈλŸ΄λ“―ν•˜κ²Œ μ•Œκ³ λ¦¬μ¦˜μ„ μ–΄λ–»κ²Œ λΆ„ν• ν•˜λ©΄μ„œ ν’€κ³  μžˆλŠλƒ ν•˜λ©΄, ꡬ간이 N/2N / 2둜 쀄어듀 λ•Œλ§ˆλ‹€, 33개의 ν•˜μœ„ λ¬Έμ œκ°€ 생기고 μžˆλ‹€. λ˜ν•œ, μ΄λŸ¬ν•œ ν•˜μœ„λ¬Έμ œλ₯Ό ν•©μΉ˜λŠ”λ° κ±Έλ¦¬λŠ” μ‹œκ°„μ΄ f(N)=2Nf(N) = 2N이닀. 이 κ²½μš°λŠ”, 인 κ²½μš°μ— μ†ν•˜κΈ° λ•Œλ¬Έμ—, μ‹œκ°„λ³΅μž‘λ„λŠ” 이 λ¬Έμ„œμ—μ„œ λ³Έ κ²ƒμ²˜λŸΌ 계산할 수 μžˆλ‹€. 결과만 보면,

이 λœλ‹€.

🏷️ 주제 λͺ©λ‘: