输入输出效率比较
一份摘自LOJ的一份读入测试数据,若干读入整数速度测试的结果(单位:毫秒)。
输入:$3×10^6$个在区间中随机生成的十进制整数。
| # | Lanuage | $[0,2)$ | $[0,8)$ | $[0,2^{15})$ | $[0,2^{31})$ | $[0,2^{63})$ | 
| fread | G++ 5.4.0 (-O2) | 13 | 13 | 39 | 70 | 111 | 
| getchar | G++ 5.4.0 (-O2) | 58 | 73 | 137 | 243 | 423 | 
| cin(关闭同步) | G++ 5.4.0 (-O2) | 161 | 147 | 205 | 270 | 394 | 
| cin | G++ 5.4.0 (-O2) | 442 | 429 | 706 | 1039 | 1683 | 
| scanf | G++ 5.4.0 (-O2) | 182 | 175 | 256 | 368 | 574 | 
下面水下一些常用的输出输出方式:
cin/cout
采用C++的特性通配流式输入输出的好处是不用处理变量类型,直接写就可以;但是根据上表可以看到,这种输入输出方式十分的耗时,并不适用于大数据编程。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | #include<iostream>using namespace std;
 int main()
 {
 int a;
 short b;
 long long c;
 cin>>a>>b>>c;
 cout<<a<<' '<<b<<' '<<c<<'\n';
 }
 
 | 
scanf/printf
采用传统C语言的输入输出的好处是节省时间,适用于大数据编程,一般的题目都可以用这种方式输入输出来通过;缺点是必须对应数据的类型而且不能少”,”,否则就会CE,scanf不能少”&”否则会导致RE,printf不能加”&”,否则会输出奇奇怪怪的东西(地址)。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | #include<cstdio>int main()
 {
 int a;
 short b;
 long long c;
 scanf("%d%hd%lld",&a,&b,&c);
 printf("%d %hd %lld\n",a,b,c);
 }
 
 | 
cin/cout(关闭同步)
我们可以通过写关闭同步的方式来加快流式输入输出,这种方式的好处是可以加快速度,甚至比刚才介绍的传统输入输出方式还要快;但是请注意:请不要在关闭同步之后采用scanf及printf输入输出,否则后果自负!
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | #include<iostream>using namespace std;
 int main()
 {
 ios::sync_with_stdio(false);
 int a;
 short b;
 long long c;
 cin>>a>>b>>c;
 cout<<a<<' '<<b<<' '<<c<<'\n';
 }
 
 | 
普通版(基于getchar/putchar)优化:
有些时候我们会因为程序的常数不够优秀而被大数据卡爆,或者我们希望暴力能够得到略微高一点的分数,这时候我们就可以采用基于getchar和putchar函数的输入输出优化。一般见到的输入优化定义的函数名是read,但是经过我多次运用,发现我一般用到的情况都是scanf被卡后才用的,所以为了方便我切换函数我使用scan定义函数。
输入优化:
| 12
 3
 4
 5
 6
 7
 8
 
 | template<typename __Type_of_scan>void scan(__Type_of_scan &x)
 {
 __Type_of_scan f=1;x=0;char s=getchar();
 while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
 while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
 x*=f;
 }
 
 | 
用法示例:
| 12
 3
 4
 5
 6
 7
 
 | int main(){
 int a;
 short b;
 long long c;
 scan(a),scan(b),scan(c);
 }
 
 | 
输出优化:
| 12
 3
 4
 5
 6
 7
 
 | template<typename __Type_of_print>void print(__Type_of_print x)
 {
 if(x<0){putchar('-');x=-x;}
 if(x>9)print(x/10);
 putchar(x%10+'0');
 }
 
 | 
用法示例:
| 12
 3
 4
 5
 6
 7
 
 | int main(){
 int a=2;
 short b=3;
 long long c=3;
 print(a),putchar(' '),print(b),putchar(' '),print(c),putchar('\n');
 }
 
 | 
黑科技(基于fread/fwrite)优化:
其实这篇博客之前一直都是基于getchar和putchar函数的输入输出优化。直到2018年9月2日我在学校的一次模拟赛中O(n)算法被毒瘤出题人的数据卡爆,我才开始使用基于fread和fwrite函数的输入输出优化。同之前所说的一样,这里所定义的函数名均为之前的优化代码前加”_”的名字,即_scan和_print。
其实fread和fwrite有个缺点就是本地测样例比较麻烦(也有可能是我写的代码太蒟蒻了),而且采用_print函数后,你需要运行fsh函数!!!
输入优化:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | char gc(){
 static char buf[100000],*p1=buf,*p2=buf;
 return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
 }
 template<typename __Type_of__scan>
 void _scan(__Type_of__scan &x)
 {
 __Type_of__scan f=1;x=0;char s=gc();
 while(s<'0'||s>'9'){if(s=='-')f=-1;s=gc();}
 while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
 x*=f;
 }
 
 | 
用法示例:
| 12
 3
 4
 5
 6
 7
 
 | int main(){
 int a;
 short b;
 long long c;
 _scan(a),_scan(b),_scan(c);
 }
 
 | 
输出优化:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | char buf[100000],*pp=buf;void pc(const char c)
 {
 if(pp-buf==100000)fwrite(buf,1,100000,stdout),pp=buf;
 *pp++=c;
 }
 template<typename __Type_of__print>
 void _print(__Type_of__print x)
 {
 if(x<0){pc('-');x=-x;}
 if(x>9)_print(x/10);
 pc(x%10+'0');
 }
 void fsh(){fwrite(buf,1,pp-buf,stdout);pp=buf;}
 
 | 
用法示例:
| 12
 3
 4
 5
 6
 7
 
 | int main(){
 int a=2;
 short b=3;
 long long c=3;
 _print(a),pc(' '),fsh(),_print(b),pc(' '),fsh(),_print(c),pc('\n'),fsh();
 }
 
 | 
关于一次性输入输出多个数
警告:可变模板仅在开启 -std=c++11 或 -std=gnu++11 时可用
Warning:variadic templates only available with -std=c++11 or -std=gnu++11  
我们以普通版(基于getchar/putchar)优化的为例
下面是一个多输入加多输出的示例
| 12
 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
 31
 32
 33
 34
 35
 36
 
 | #include<bits/stdc++.h>using namespace std;
 template<typename tpos>
 void scan(tpos &x)
 {
 tpos f=1;x=0;char s=getchar();
 while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
 while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
 x*=f;
 }
 template<typename tpos,typename... Tpos>
 void scan(tpos &x,Tpos&... X)
 {
 scan(x),scan(X...);
 }
 template<typename tpop>
 void print(tpop x)
 {
 if(x<0)putchar('-'),x=-x;
 if(x>9)print(x/10);
 putchar(x%10+'0');
 }
 template<typename tpop,typename... Tpop>
 void print(tpop x,Tpop... X)
 {
 print(x),putchar(' '),print(X...);
 }
 int main()
 {
 int a;
 short b;
 long long c;
 scan(a,b,c);
 print(a,b,c),putchar('\n');
 return 0;
 }
 
 | 
简单来说,就是在scan函数的下面加上
| 12
 3
 4
 5
 
 | template<typename tpos,typename... Tpos>void scan(tpos &x,Tpos&... X)
 {
 scan(x),scan(X...);
 }
 
 | 
在print函数的下面加上
| 12
 3
 4
 5
 
 | template<typename tpop,typename... Tpop>void print(tpop x,Tpop... X)
 {
 print(x),putchar(' '),print(X...);
 }
 
 | 
然后调用
| 12
 
 | scan(a,b,c);print(a,b,c),putchar('\n');
 
 | 
事实上,你也可以类比此普通版优化,推出黑科技优化的多输入输出
下面就是一个例子
| 12
 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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 
 | #include<bits/stdc++.h>using namespace std;
 char gc()
 {
 static char buf[100000],*p1=buf,*p2=buf;
 return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
 }
 template<typename tpo_s>
 void _scan(tpo_s &x)
 {
 tpo_s f=1;x=0;char s=gc();
 while(s<'0'||s>'9'){if(s=='-')f=-1;s=gc();}
 while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
 x*=f;
 }
 template<typename tpo_s,typename... Tpo_s>
 void _scan(tpo_s &x,Tpo_s &...X)
 {
 _scan(x),_scan(X...);
 }
 char buf[100000],*pp=buf;
 void pc(const char c)
 {
 if(pp-buf==100000)fwrite(buf,1,100000,stdout),pp=buf;
 *pp++=c;
 }
 template<typename tpo_p>
 void _print(tpo_p x)
 {
 if(x<0){pc('-');x=-x;}
 if(x>9)_print(x/10);
 pc(x%10+'0');
 }
 void fsh(){fwrite(buf,1,pp-buf,stdout);pp=buf;}
 template<typename tpo_p,typename... Tpo_p>
 void _print(tpo_p x,Tpo_p ...X)
 {
 _print(x),pc(' '),_print(X...);
 }
 int main()
 {
 int a;
 short b;
 long long c;
 _scan(a,b,c);
 _print(a,b,c),pc('\n'),fsh();
 return 0;
 }
 
 | 
事实上,这个也就是在_scan函数的下面加上
| 12
 3
 4
 5
 
 | template<typename tpo_s,typename... Tpo_s>void _scan(tpo_s &x,Tpo_s &...X)
 {
 _scan(x),_scan(X...);
 }
 
 | 
在print函数的下面加上
| 12
 3
 4
 5
 
 | template<typename tpo_p,typename... Tpo_p>void _print(tpo_p x,Tpo_p ...X)
 {
 _print(x),pc(' '),_print(X...);
 }
 
 | 
然后调用
| 12
 
 | _scan(a,b,c);_print(a,b,c),pc('\n'),fsh();
 
 | 


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。