티스토리 뷰
카라츠바 알고리즘의 간략한 설명이다.
원래 a×b를 할 때 시간복잡도는 O(N2)이다. (a, b의 길이는 모두 N)
이를 Divide & Conquer 로 줄일 수 있는데,
편의를 위하여 N=20이라 하자.

구현이 젤 중요하다.
1. LIM : 어느정도 이상은 카라츠바의 상수가 매우 크기 때문에 O(N2)의 곱셈이 유리하다. 나는 이를 1000정도로 설정했다.
2. CUT : 이게 젤 중요하다. 그냥 각 자리 수를 벡터에 넣고 카라츠바를 돌리면 O(Nlog3) 에 300,000을 넣은 값으로 10초 정도 걸려야 답이 나온다. 하지만 8자리씩 묶어서 넣으면 그만큼 N이 줄어듬으로 훨씬 낫다. 나는 이를 1e8정도로 설정해서 1억 진법을 따랐다.
3. 포인터 주고받기 : 그냥 벡터를 함수 인자로 주고받지 않고, const vector<int>& a; 이런 식으로 참조형을 주고받아 시간을 절약했다.

최대 248 ms 까지 절약할 수 있었다.
시간 복잡도 : O(Nlog3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define MAXN 300000
#define CUT 8
#define DEC 100000000
#define LIM 1000
string sa, sb;
vector<ll> va, vb;
void convert(string s, vector<ll>& v)
{
int i, j;
for(i=s.size()-1; i>=0; i-=CUT)
{
ll now=0;
for(j=max(i-CUT+1, 0); j<=i; j++) now=now*10+s[j]-'0';
v.push_back(now);
}
}
vector<ll> mult(const vector<ll>& a, const vector<ll>& b)
{
vector<ll> ret(a.size()+b.size()+1, 0);
int i, j;
for(i=0; i<a.size(); i++) for(j=0; j<b.size(); j++) ret[i+j]+=a[i]*b[j];
for(i=0; i<ret.size(); i++) if(ret[i]>=DEC)
{
ret[i+1]+=ret[i]/DEC;
ret[i]%=DEC;
}
return ret;
}
void addTo(vector<ll>& a, const vector<ll>& b, int k)
{
if(a.size()<b.size()+k) a.resize(b.size()+k);
a.push_back(0);
int i;
for(i=0; i<b.size(); i++) a[i+k]+=b[i];
for(i=0; i<a.size(); i++) if(a[i]>=DEC)
{
a[i+1]+=a[i]/DEC;
a[i]%=DEC;
}
for(i=a.size()-1; i>=0 && a[i]==0; i--) a.pop_back();
}
void subFrom(vector<ll>& a, const vector<ll>& b)
{
int i;
for(i=0; i<b.size(); i++) a[i]-=b[i];
for(i=0; i<a.size(); i++) if(a[i]<0)
{
a[i]+=DEC;
a[i+1]-=1;
}
for(i=a.size()-1; i>=0 && a[i]==0; i--) a.pop_back();
}
vector<ll> karatsuba(const vector<ll>& a, const vector<ll>& b)
{
int sa = a.size(), sb = b.size();
if(sa<sb) return karatsuba(b, a);
if(sa==0 || sb==0) return vector<ll>();
if(sa<=LIM) return mult(a, b);
int mid = sa/2;
vector<ll> a0 = vector<ll>(a.begin(), a.begin()+mid);
vector<ll> a1 = vector<ll>(a.begin()+mid, a.end());
vector<ll> b0 = vector<ll>(b.begin(), min(b.begin()+mid, b.end()));
vector<ll> b1 = vector<ll>(min(b.begin()+mid, b.end()), b.end());
vector<ll> z0 = karatsuba(a0, b0);
vector<ll> z1 = karatsuba(a1, b1);
addTo(a0, a1, 0);
addTo(b0, b1, 0);
vector<ll> z2 = karatsuba(a0, b0);
subFrom(z2, z0);
subFrom(z2, z1);
vector<ll> ret;
addTo(ret, z0, 0);
addTo(ret, z2, mid);
addTo(ret, z1, mid+mid);
return ret;
}
void print(vector<ll>& v)
{
int i, j;
//for(i=0; i<v.size(); i++) printf("%lld ", v[i]); printf("\n");
for(i=v.size()-1; i>=0 && v[i]==0; i--) v.pop_back();
if(v.empty()) { printf("0"); return; }
printf("%lld", v[i]);
for(i--; i>=0; i--)
{
for(j=DEC/10; j>=1; j/=10) printf("%lld", v[i]/j%10);
}
}
int main()
{
cin.tie(0); cout.tie(0);
ios_base::sync_with_stdio(false);
cin>>sa>>sb;
convert(sa, va);
convert(sb, vb);
vector<ll> ans = karatsuba(va, vb);
print(ans);
return 0;
}
'알고리즘 & 자료구조 정리' 카테고리의 다른 글
트리의 지름 (0) | 2019.01.02 |
---|---|
세그먼트 트리 (Segment Tree) (0) | 2018.12.27 |
LIS (Longest Increasing Subsequence) (0) | 2018.12.17 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- CHT
- Sparse Table
- Segment Tree
- stack
- BOJ
- APIO
- Parametric Search
- convex hull
- Persistent Segment Tree
- DFS
- Floyd-Warshall
- Lazy Propagation
- Centroid Decomposition
- ioi
- HLD
- Merge Sort
- Sqrt Decomposition
- tree
- Union Find
- graph
- Shortest path
- Divide & Conquer
- ⭐
- offline
- Codeforces
- Fenwick Tree
- Line sweeping
- Greedy
- DP
- Interactive
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
글 보관함