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

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

2015年辽宁省赛热身赛有一道高精度乘法

传送门:NEUOJ 1574 A*B

1574: A * B

时间限制: 10 Sec  内存限制: 128 MB

题目描述

Calculate $a imes b$.

输入

Your program will be tested on one or more test cases. In each test case, two integer $a$, $b$ ($0le a,ble 10^{100000}$).

 

输出

For each test case, print the following line:

answer

where answer is $a imes b$.

样例输入

1000000000000000 2000000000000000

样例输出

2000000000000000000000000000000

提示

来源

2015省赛热身赛

朴素高精度乘法的典型写法是

 1 #include
 2 #include
 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
 2 #include
 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){
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){
38             for(int j=0; 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
 2 #include
 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){
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){
38             for(int j=0; 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

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • 问题链接 LeetCode 7 题目解析 给定一个32位有符号整数,求其反转数字。 解题思路 如果是简单反转的话,那这道题就太简单了。题目要求判断溢出问题,32位int类型的范围是-2147483648~2147483647。数字反转过后是有可能超出范围的,此时应该返回0。 最简单的想法是,反转结果用long long表示,其范围远超...

  • 这两天在看小程序的地图,写写笔记记录一下 小程序官方文档提供的方法 https://mp.weixin.qq.com/debug/wxadoc/dev/api/location.html 腾讯地图提供的jssdk http://lbs.qq.com/qqmap_wx_jssdk/qqmapwx.html 根据提示使用腾讯地图jssdk...

  • 1.链接地址: http://bailian.openjudge.cn/practice/2739/ 2.题目: 总时间限制:1000ms内存限制:65536kB描述给定两个正整数a和b。可以知道一定存在整数x,使得x <= logab < x + 1输出x输入第1行是测试数据的组数n,每组测试数据占2行,分别是a和b。每组测试数据之...

  • 用python编写乘法口诀表的方法 发布时间:2020-08-25 11:46:35 来源:亿速云 阅读:60 作者:小新 用python编写乘法口诀表的方法?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧! 第一种:使用for遍历循环嵌套for x in...

  • //很长一段时间我都只使用以下方式做数组循环,具体原因看数据 var aa = for (var i = 0, l = aa.length; i < l; i++) { var a = aa[i];} 数据采集图片来源于网友 很明显,for循环第二种方式完胜!!! 至于for in、forEach什么的,不知道甩他们多少...

  • 目录 1. Scene Graph Generation with External Knowledge and Image Reconstruction 2. Knowledge Acquisition for Visual Question Answering via Iterative Querying Author...

  • 基础题1: 输入一个正整数 n (1≤n≤10)和n 阶方阵a的元素,如果方阵a中的所有元素都沿主对角线对称,输出“Yes”, 否则,输出“No”。主对角线为从矩阵的左上角至右下角的连线,方阵a中的所有元素都沿主对角线对称指对所有i, k,a[i][k]和a[k][i]相等。输入输出示例如下: 输入: 3 1 2 3 4 5 6 7...

  • 程序流程控制 分支 顺序 循环 if switch&case 1 2 3 调整 break 1.6 前 switch(byte、short、char、int) 1.7 可放String 循环 while(次数不确定) do while for(确定次数) break(跳出本层循环) continue(跳出本次循环)     *   2...