震惊!C++用不同的方式输入,时间相差20倍!原因竟然是……

upd:之前的界面太丑,美化了一下

前言

众所周知, C + + C++ C++的流输入输出 c i n cin cin c o u t cout cout是比较慢的。在比赛(尤其像 O I OI OI这种有部分分的比赛)中,用 c i n cin cin c o u t cout cout有可能超时,而意味着有些本该拿到的分就拿不到了。这里给出 c i n cin cin s c a n f scanf scanf,手写 r e a d read read,优化手写 r e a d read read的速度对比。

制造数据

先做一个数据生成器。用文件输出,将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个数据需要读入时,程序肯定就超时了。
所以,必须要想法将输入的速度优化。
c i n cin cin的能够与 s c a n f scanf scanf混用的原理,是开启了同步,大大拖慢了程序运行的时间(这个很多博客里也写过,就不详细讲了)。
于是,关闭同步再测(关闭同步用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。速度已经提升到原来的两倍。
但是在 N O I L i n u x NOI Linux NOILinux的环境下,关闭流同步是编译不过的!
所以,比赛中需要用其他的优化。
就可以想到用scanf。

scanf的速度

s c a n f scanf scanf的速度比(不关闭同步的) c i n cin cin的速度要快很多。
程序如下:

#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。
可见, s c a n f scanf scanf也比 c i n cin cin快一倍左右,但比关闭流输入的 c i n cin cin要慢一点。
但是, s c a n f scanf scanf碰见输入数据很多的情况,也是会TLE的。因为这时留给程序的计算时间已经不多了。

手写read的速度

g e t c h a r getchar getchar是一个快速读入字符的函数。由于其速度快,经常用它来手写 r e a d read read函数。函数的思路是:遇到不相关的字符,跳过;遇到数字,则将它放进预先设置的一个整型变量的尾端。
程序如下:

#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,效率比 s c a n f scanf scanf和关闭流输入的cin都要高很多。
推荐使用这种 r e a d read read,代码量不大,背起来方便,考场上敲起来也不是很困难。
而且在OI中先敲一个13行的read也能给旁边的人造成压力(大雾)

优化手写read的速度

有神犇提出, r e a d read read还可以优化,原因是 g e t c h a r getchar getchar没有 f r e a d fread fread快!
S R O S R O S R O SROSROSRO SROSROSRO d a l a o dalao dalao O T Z O T Z O T Z OTZOTZOTZ OTZOTZOTZ
程序如下:

#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,是本人所知道的最快的输入方法。
不过这种东西太过玄学……

个人推荐用手写 r e a d read read不加getc的优化

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值