論整數輸入優化

背景

前些天發現了寫讀入優化和不寫讀入優化的區別。。。

別人的代碼:

我的代碼:

我似乎發現了什麼東西。。。

然後我點進第一名的代碼一看:

void get(int &res){ 
    char ch;bool flag=0; 
    while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true); 
    for(res=num;isdigit(ch=getchar());res=res*10+num); 
    (flag)&&(res=-res); 
}

當時我是懵逼的。小小一個函數,竟然可以快那麼多!
在分析函數的時候,我弄不明白(ch=='-')&&(flag=true)(flag)&&(res=-res)是什麼意思,然後我就分析了一下:

(A)&&(B)是先判斷A是否成立,如果A成立,B爲執行的語句(如a=b),就執行B語句。

這就是讀入優化
然後我搜集了一下資料,這一種寫法是比較裝逼,會遭雷劈的寫法,所以我們還是腳踏實地,從零開始。

讀入優化的原理與實現

C++裏有很多種輸入方式,我們最常用的是scanf和cin,因爲這兩個函數比較好用一些。除此之外,還有一些讀入字符的函數,給大家普及一下:

#include<cstdio>
#include<cstring>
int main()
{
    char c[];
    int len=strlen(c);//獲取c數組的長度,需要用#include<cstring>頭文件

    gets(c);//讀入一行字符串,遇到回車後停止,可以無限讀取,不會判斷上限,所以容易溢出。而且該函數,這個函數在ISO/IEC 9899 2011(C11)標準中被移除。

    fgets(c,len,stdin);//和gets函數差不多,讀取長度超過len或者遇到回車都會停下來,所以每次最多讀取len-1個(因爲字符數組最後一個'\0'佔位)

    c[0]=getchar();//像scanf一樣,讀入一個字符,並返回這個字符,可以直接賦值,需要用#include<cstdio>頭文件。和scanf不同的是,該函數是非緩衝輸入函數,也就意味着它比scanf更快。

    c[0]=getch();//直接讀入一個字符,而沒有回顯(但在linux系統下有回顯)。也就是說,你讀入了一個字符,它不會在界面裏顯示而是直接讀入這個字符,getch也會直接返回這個字符。比如說你寫了一個程序小遊戲,你肯定不希望看到wasd滿天飛,所以就用getch。需要#include<conio.h>頭文件。同樣,它沒有緩衝。
}

getchar比scanf更快。

我們可不可以用getchar來改進我們的輸入呢?

當然是可以的。首先getchar是一個一個字符讀入的,所以我們要一個一個讀入,但是我們要給他進位,就要乘以10,因爲它是ASCII碼,就要給它減個‘0’。比如是字符‘1’,‘1’-‘0’就爲數字1.

理論了過後,我們就來模擬一下過程:
假設輸入2018。

  1. 程序讀入字符‘2’,‘2’-‘0’得數字2,ans=0*10+2=2.
  2. 程序讀入字符‘0’,‘0’-‘0’得數字0,ans=2*10+0=20.
  3. 程序讀入字符‘1’,‘1’-‘0’得數字1,ans=20*10+1=201.
  4. 程序讀入字符‘8’,‘8’-‘0’得數字8,ans=201*10+8=2018.

基本上就是這樣,不給大家一一舉例了。
依照以上的邏輯,ans=ans*10+ch-'0'

可是有的時候輸入這樣子:
123 456 789

每兩個數之間有空格,這又怎麼辦呢?

哦,那麼就先把數字前的空格讀完,即一直讀入,直到出現數字爲止。
到此我們就可以給出第一代的程序了:

void get(int &a)
{
    a=0;
    char ch;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') a=a*10+ch-'0',ch=getchar();
}

遇到負數怎麼處理?

相信有些讀者會說直接將數字乘以-1,可是一開始數字爲0,乘以任何數都爲0,肯定是不能這樣做的。
那只要在最開始設置標記爲1,讀整數之前,如果有符號(‘-’),就設置標記爲-1,最終用數字乘以標記即可。

肯定有人要說了,那開頭的isdigit函數是什麼鬼?
其實這個函數就是檢測字符爲不爲阿拉伯數字0到9,所以我們也可以用這個函數減小代碼量。
注:要用#include<iostream>頭文件。

代碼

最終有兩種代碼。

void get(int &p){
    int flag=1;char ch;
    for(p=0;!isdigit(ch);ch=getchar()) if(ch=='-') flag=-1;
    for(;isdigit(ch);ch=getchar()) p=p*10+ch-'0';
    p*=flag;
}

這種代碼直接get(int)即可。

int get(){
    int flag=1,p=0;char ch;
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') flag=-1;
    for(;isdigit(ch);ch=getchar()) p=p*10+ch-'0';
    return p*flag;
}

這種代碼int=get()即可。

驗證讀入優化的效率

數據製造程序

用freopen生成1000000個數的文本,並分別用scanf,cin和讀入優化讀入。

#include<cstdio>
const int N=1000000;
int main()  
{  
    freopen("test.txt","w",stdout);//保存輸出結果爲文件
    for(int i=1;i<=4;i++)//測試直接get,賦值get,scanf,cin四種讀入程序
    {
        for(int j=1;j<=N;j++)  
            printf("%d ",i);
        puts("");
    }
} 

測試程序

#include<ctime>
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1000000;
void get_tradition(int &p){
    int flag=1;char ch;
    for(p=0;!isdigit(ch);ch=getchar()) if(ch=='-') flag=-1;
    for(;isdigit(ch);ch=getchar()) p=p*10+ch-'0';
    p*=flag;
}
int get_assignment(){
    int flag=1,p=0;char ch;
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') flag=-1;
    for(;isdigit(ch);ch=getchar()) p=p*10+ch-'0';
    return p*flag;
}
int main()
{
    freopen("test.txt","r",stdin);//讀入測試文件
    freopen("test.out","w",stdout);
    int x;
    double A[4];
    double t1=clock();
    for(int i=1;i<=N;i++)
        get_tradition(x);
    double t2=clock();
    A[0]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=N;i++)
        x=get_assignment();
    t2=clock();
    A[1]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=N;i++)
        scanf("%d",&x);
    t2=clock();
    A[2]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=N;i++)
        cin>>x;
    t2=clock();
    A[3]=(t2-t1)/1000;
    fclose(stdout);
    freopen("Answer.out","w",stdout);
    printf("When number is %d,\n",N);
    printf("get_tradition   took %.3lf second(s)\n",A[0]);
    printf("get_assignment  took %.3lf second(s)\n",A[1]);
    printf("scanf           took %.3lf second(s)\n",A[2]);
    printf("cin             took %.3lf second(s)\n",A[3]);
}

總結

When number is 1000000,
get_tradition took 0.078 second(s)
get_assignment took 0.062 second(s)
scanf took 0.297 second(s)
cin took 2.063 second(s)

cena

作死,n=100000000時,

表示cin無語。。。

讀入優化並不是裝逼,而真正可以節省時間。所以讀入優化是針對數據比較多的題目而言的,所以讓我們養成寫讀入優化的習慣!

發佈了80 篇原創文章 · 獲贊 134 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章