朴素高精度乘法的常数优化
2015年遼寧省賽熱身賽有一道高精度乘法
傳送門:NEUOJ 1574 A*B
1574: A * B
時間限制: 10 Sec??內(nèi)存限制: 128 MB題目描述
Calculate $a \times b$.
輸入
Your?program?will?be?tested?on?one?or?more?test?cases.?In?each?test?case,?two?integer $a$, $b$ ($0\le a,b\le 10^{100000}$).
?
輸出
For?each?test?case,?print?the?following?line:
answer
where?answer?is $a\times b$.
樣例輸入
1000000000000000 2000000000000000 樣例輸出
2000000000000000000000000000000 提示
來源
2015省賽熱身賽
樸素高精度乘法的典型寫法是
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int MAX_N=1e5+10; 5 char sa[MAX_N], sb[MAX_N]; 6 int a[MAX_N], b[MAX_N], res[MAX_N<<1]; 7 int main(){ 8 //freopen("in", "r", stdin); 9 while(~scanf("%s%s", sa, sb)){ 10 int i, j; 11 memset(res, 0, sizeof(res)); 12 for(i=0; sa[i]; i++){ 13 for(j=0; sb[j]; j++){ 14 res[i+j]+=(sa[i]-'0')*(sb[j]-'0'); 15 } 16 } 17 int tot=i+j-2; //error-prone 18 for(i=tot; i; i--){ 19 res[i-1]+=res[i]/10; 20 res[i]%=10; 21 } 22 printf("%d", res[0]); //error-prone 23 if(res[0]) for(i=1; i<=tot; i++) printf("%d", res[i]); 24 puts(""); 25 } 26 return 0; 27 }
但這樣寫會TLE。下面要介紹一種常數(shù)優(yōu)化:
將大整數(shù)從低位到高位每 $6$ 位分成一組(最后一組若不夠 $6$ 位自動在高位補(bǔ) $0$),這樣便自然得到了大整數(shù)的「一百萬進(jìn)制」表示,將每組內(nèi)的十進(jìn)制計數(shù)看成是一百萬進(jìn)制的一個“數(shù)字”,由于小于一百萬的數(shù)的乘法無需采用高精度計算,可認(rèn)為是 $O(1)$ 的,實際上對于不致溢出的小整數(shù),機(jī)器實現(xiàn)的乘法比模擬豎式計算要快好多。這樣便在原來十進(jìn)制表示下的樸素高精度乘法的基礎(chǔ)上,實現(xiàn)了常數(shù)優(yōu)化。這樣的常數(shù)優(yōu)化常常是有效的。
1 #include<cstdio> 2 #include<cstring> 3 #define set0(a) memset(a, 0, sizeof(a)) 4 using namespace std; 5 const int MAX_N=1e5+10; 6 typedef long long ll; 7 char sa[MAX_N], sb[MAX_N]; 8 ll a[MAX_N], b[MAX_N], res[MAX_N<<1]; 9 int base=6; 10 ll mod=1e6; 11 int trans(char *s, ll *a){ //value-passed 12 //memset(a, 0, sizeof(a)); //error-prone 13 int ls=strlen(s), len=(ls+base-1)/base; 14 int now=0, rem=ls%base; 15 int l, r; 16 if(rem){ 17 l=0; r=rem; 18 for(int i=r-1, p=1; i>=l; i--, p*=10){ 19 a[0]+=(s[i]-'0')*p; 20 } 21 now++; 22 } 23 for(int i=0; now<len; now++, i++){ 24 l=rem+base*i, r=l+base; //error-prone 25 for(int j=r-1, p=1; j>=l; j--, p*=10){ 26 a[now]+=(s[j]-'0')*p; 27 } 28 } 29 return len; 30 } 31 32 int main(){ 33 //freopen("in", "r", stdin); 34 while(~scanf("%s%s", sa, sb)){ 35 set0(a); set0(b); set0(res); 36 int la=trans(sa, a), lb=trans(sb, b); 37 for(int i=0; i<la; i++){ 38 for(int j=0; j<lb; j++){ 39 res[i+j]+=a[i]*b[j]; 40 } 41 } 42 int tot=la+lb-2; 43 for(int i=tot; i; i--){ 44 res[i-1]+=res[i]/mod; 45 res[i]%=mod; 46 } 47 int i; 48 for(i=0; i<=tot&&!res[i]; i++); 49 if(i>tot) putchar('0'); 50 else{ 51 printf("%lld", res[i++]); //error-prone 52 for(; i<=tot; i++) printf("%06lld", res[i]); 53 } 54 puts(""); 55 } 56 return 0; 57 }
Time: 4158MS
選擇每 $6$ 個分一組是要保證運算過程不會溢出 long long,就本題的數(shù)據(jù)范圍而言,取 $7$ 位分為一組也可,這樣常數(shù)會更優(yōu),只需將上面的代碼稍加修改(注意加粗的三行)
1 #include<cstdio> 2 #include<cstring> 3 #define set0(a) memset(a, 0, sizeof(a)) 4 using namespace std; 5 const int MAX_N=1e5+10; 6 typedef long long ll; 7 char sa[MAX_N], sb[MAX_N]; 8 ll a[MAX_N], b[MAX_N], res[MAX_N<<1]; 9 int base=7; 10 ll mod=1e7; 11 int trans(char *s, ll *a){ //value-passed 12 //memset(a, 0, sizeof(a)); //error-prone 13 int ls=strlen(s), len=(ls+base-1)/base; 14 int now=0, rem=ls%base; 15 int l, r; 16 if(rem){ 17 l=0; r=rem; 18 for(int i=r-1, p=1; i>=l; i--, p*=10){ 19 a[0]+=(s[i]-'0')*p; 20 } 21 now++; 22 } 23 for(int i=0; now<len; now++, i++){ 24 l=rem+base*i, r=l+base; //error-prone 25 for(int j=r-1, p=1; j>=l; j--, p*=10){ 26 a[now]+=(s[j]-'0')*p; 27 } 28 } 29 return len; 30 } 31 32 int main(){ 33 //freopen("in", "r", stdin); 34 while(~scanf("%s%s", sa, sb)){ 35 set0(a); set0(b); set0(res); 36 int la=trans(sa, a), lb=trans(sb, b); 37 for(int i=0; i<la; i++){ 38 for(int j=0; j<lb; j++){ 39 res[i+j]+=a[i]*b[j]; 40 } 41 } 42 int tot=la+lb-2; 43 for(int i=tot; i; i--){ 44 res[i-1]+=res[i]/mod; 45 res[i]%=mod; 46 } 47 int i; 48 for(i=0; i<=tot&&!res[i]; i++); 49 if(i>tot) putchar('0'); 50 else{ 51 printf("%lld", res[i++]); //error-prone 52 for(; i<=tot; i++) printf("%07lld", res[i]); 53 } 54 puts(""); 55 } 56 return 0; 57 }
?Time: 2937MS(這結(jié)果在所有AC的提交中算是很優(yōu)的了)
當(dāng)然高精度乘法有復(fù)雜度更優(yōu)的解法,比如快速傅立葉變換(FFT),但通過本題,我們看到這種代碼量較小的分組常數(shù)優(yōu)化對于 $N$ 不太大,時限較寬的問題還是很有效的。
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/Patt/p/4625353.html
總結(jié)
以上是生活随笔為你收集整理的朴素高精度乘法的常数优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求答案歌词
- 下一篇: 出品人是否代表影片的投资人