日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 默认站点!

默认站点

當前位置: 首頁 >

朴素高精度乘法的常数优化

發布時間:2023/11/27 32 豆豆
默认站点 收集整理的這篇文章主要介紹了 朴素高精度乘法的常数优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2015年遼寧省賽熱身賽有一道高精度乘法

傳送門:NEUOJ 1574 A*B

1574: A * B

時間限制: 10 Sec??內存限制: 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。下面要介紹一種常數優化:

將大整數從低位到高位每 $6$ 位分成一組(最后一組若不夠 $6$ 位自動在高位補 $0$),這樣便自然得到了大整數的「一百萬進制」表示將每組內的十進制計數看成是一百萬進制的一個“數字”,由于小于一百萬的數的乘法無需采用高精度計算,可認為是 $O(1)$ 的,實際上對于不致溢出的小整數,機器實現的乘法比模擬豎式計算要快好多。這樣便在原來十進制表示下的樸素高精度乘法的基礎上,實現了常數優化。這樣的常數優化常常是有效的。

 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,就本題的數據范圍而言,取 $7$ 位分為一組也可,這樣常數會更優,只需將上面的代碼稍加修改(注意加粗的三行)

 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(這結果在所有AC的提交中算是很優的了)

當然高精度乘法有復雜度更優的解法,比如快速傅立葉變換(FFT),但通過本題,我們看到這種代碼量較小的分組常數優化對于 $N$ 不太大,時限較寬的問題還是很有效的。

?

?

?

?

轉載于:https://www.cnblogs.com/Patt/p/4625353.html

總結

以上是默认站点為你收集整理的朴素高精度乘法的常数优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得默认站点網站內容還不錯,歡迎將默认站点推薦給好友。