upd:之前的界面太丑,美化了一下
前言
众所周知,C++C++C++的流输入输出cincincin和coutcoutcout是比较慢的。在比赛(尤其像OIOIOI这种有部分分的比赛)中,用cincincin和coutcoutcout有可能超时,而意味着有些本该拿到的分就拿不到了。这里给出cincincin,scanfscanfscanf,手写readreadread,优化手写readreadread的速度对比。
制造数据
先做一个数据生成器。用文件输出,将5000000个随机数保存在data.txt里,后面的程序都从data.txt中读取数据。
如下:
#include<stdio.h>
#include<stdlib.h>
int main() {
freopen("data.txt","w",stdout);
const int Size=5000000;
for(int i=1; i<=Size; i++)
printf("%d\n",rand());
return 0;
}
cin的速度
先写出一个用cin读入这5000000个数的程序,用time.h里面的clock()函数算出程序运行用了多少时间。
程序如下:
#include<iostream>
#include<time.h>
#include<stdio.h>
using namespace std;
int main() {
freopen("data.txt","r",stdin);
const int Size=5000000;
int a,b,n;
a=clock();
for(int i=1; i<=Size; i++) {
cin>>n;
}
b=clock();
printf("%d ms",b-a);
return 0;
}
程序的运行结果为10667ms,即约10.7秒。
假设时限1秒,可算出当有500000个数据需要读入时,程序肯定就超时了。
所以,必须要想法将输入的速度优化。
cincincin的能够与scanfscanfscanf混用的原理,是开启了同步,大大拖慢了程序运行的时间(这个很多博客里也写过,就不详细讲了)。
于是,关闭同步再测(关闭同步用ios::sync_with_stdio(false)):
程序为
#include<iostream>
#include<time.h>
using namespace std;
int main() {
freopen("data.txt","r",stdin);
std::ios::sync_with_stdio(false);
const int Size=5000000;
int a,b,n;
a=clock();
for(int i=1; i<=Size; i++) {
cin>>n;
}
b=clock();
printf("%d ms",b-a);
return 0;
}
运行结果:4927ms。速度已经提升到原来的两倍。
但是在NOILinuxNOI LinuxNOILinux的环境下,关闭流同步是编译不过的!
所以,比赛中需要用其他的优化。
就可以想到用scanf。
scanf的速度
scanfscanfscanf的速度比(不关闭同步的)cincincin的速度要快很多。
程序如下:
#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
int main() {
freopen("data.txt","r",stdin);
const int Size=5000000;
int a,b,n;
a=clock();
for(int i=1; i<=Size; i++) {
scanf("%d",&n);
}
b=clock();
printf("%d ms",b-a);
return 0;
}
运行结果:5232ms。
可见,scanfscanfscanf也比cincincin快一倍左右,但比关闭流输入的cincincin要慢一点。
但是,scanfscanfscanf碰见输入数据很多的情况,也是会TLE的。因为这时留给程序的计算时间已经不多了。
手写read的速度
getchargetchargetchar是一个快速读入字符的函数。由于其速度快,经常用它来手写readreadread函数。函数的思路是:遇到不相关的字符,跳过;遇到数字,则将它放进预先设置的一个整型变量的尾端。
程序如下:
#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
int read() {
int x=0,f=1; //x是返回的数字,f表示正负。
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1; //处理负数
ch=getchar(); //跳过这个字符
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
int main() {
freopen("data.txt","r",stdin);
const int Size=5000000;
int a,b,n;
a=clock();
for(int i=1; i<=Size; i++) {
n=read();
}
b=clock();
printf("%d ms",b-a);
return 0;
}
运行结果:1639ms,效率比scanfscanfscanf和关闭流输入的cin都要高很多。
推荐使用这种readreadread,代码量不大,背起来方便,考场上敲起来也不是很困难。
而且在OI中先敲一个13行的read也能给旁边的人造成压力(大雾)
优化手写read的速度
有神犇提出,readreadread还可以优化,原因是getchargetchargetchar没有freadfreadfread快!
SROSROSROSROSROSROSROSROSRO dalaodalaodalao OTZOTZOTZOTZOTZOTZOTZOTZOTZ
程序如下:
#include<iostream>
#include<stdio.h>
#include<time.h>
using namespace std;
inline char getc() { //优化的getchar
static char buf[262145],*fs,*ft;
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}
int read() {
int x=0,f=1;
char ch=getc();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getc();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getc();
}
return x*f;
}
int main() {
freopen("data.txt","r",stdin);
const int Size=5000000;
int a,b,n;
a=clock();
for(int i=1; i<=Size; i++) {
n=read();
}
b=clock();
printf("%d ms",b-a);
return 0;
}
运行结果达到了487ms,是本人所知道的最快的输入方法。
不过这种东西太过玄学……
个人推荐用手写readreadread(不加getc的优化)