最近看到洛谷上面有一個讀入優化的代碼:git
inline char get_char(){//勁者快讀 static char buf[1000001],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; } inline short read(){ short num=0; char c; while(isspace(c=get_char())); while(num=num*10+c-48,isdigit(c=get_char())); return num; }
說實話第一個函數get_char的第二行,這麼長一六三目運算符真心看不懂數組
(下面的read函數裏面那個isspace()和isdigit()就是判斷這個字符是否是空格,是否是數字,是的就返回true,不是返回false。你看多沒用的函數= =)函數
而後我就把代碼百度了一下,發現還真有相似的東西:測試
inline char NC(void) { static char buf[100000], *p1 = buf, *p2 = buf; if (p1 == p2) { p2 = (p1 = buf) + fread(buf, 1, 100000, stdin); if (p1 == p2) return EOF; } return *p1++; }
因而研究了一會,發現這是一個極其神奇的讀入優化優化
通常來講咱們讀入都用的scanf和cin,實在必要的時候能夠用getchar讀入優化。spa
而後衆所周知,cin比scanf慢,getchar最快。指針
可是到底差距有多大不少人都不知道。code
因而上個星期我作了一個賊有意思的測試,把scanf、cin、getchar(分爲宏定義函數和內聯函數兩個)分別讀入1000000個數,而後輸出運行時間。blog
getchar的兩個函數貼在這裏:ci
#define gi(a) do { \ register char ch; \ while((ch = getchar()) > '9' || ch < '0'); \ for(a = ch-'0'; (ch = getchar()) >= '0' && ch <= '9'; a = a*10+ch-'0'); \ }while(0) inline void gi2(int &a) { register char ch; while((ch = getchar()) > '9' ||ch < '0'); for(a = ch-'0'; (ch = getchar()) >= '0' && ch <= '9'; a = a*10+ch-'0'; } //(僅限正整數)
發現scanf大概比cin快2倍,getchar比cin快5倍。
其中宏定義的getchar稍稍比內聯的getchar快那麼一點點點點。
而後就是今天我看到的玄學優化了。
https://www.byvoid.com/zhs/blog/fast-readfile
這裏有全部讀入的速度評估(看來我還不是第一個幹這種賊有意思的事情的wwww)
通過實測,這種優化比scanf快了10倍!
這個讀入用的是fread,我百度了一下,這是一種直接把文件全部字符所有讀入的一個函數。
函數原型是這樣的:
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
能夠看到有四個參數,分別是讀入的數組,讀入每一個數據項的字節數,讀入的個數,以及讀入的文件stream。
返回的是讀到的數據項的個數。
此外還有個static關鍵字,就把它當作是函數裏面的全局變量就好了,也就是說再用一次這個函數,裏面的值不會變的。
此外static只有在第一次定義的時候才能賦值,以後調用的時候賦值是無效的,也就是說以後的調用函數這個賦值那一行是沒用的。
這裏還有個點,看這一行:
p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
這裏的=號的返回值就是賦值號右邊的那個值,也就是buf的值。
那麼就等於寫成這樣:
p1 = buf; p2 = buf+ fread(buf, 1, 100000, stdin);
在咱們一開始調用函數的時候,p1 p2都被賦值爲buf的數組開始的位置,以後進入if語句,p2變成數據最後的位置(開始的位置+數據的長度 = 最後的位置)。
而後隨着每次返回,p1都會往前面移動一個,直到p1也遍歷到了最後的位置,p1 == p2
這時候再次進入if語句,而後一樣p2變成數據最後的位置,由於p1這時也是數據最後的位置了,因此p1照樣==p2,因而數據遍歷結束了,返回文件結束符EOF.
就這樣,完美模擬一次文件讀寫~
而後把if語句簡化就變成了那個很長一六的三目運算符:
inline char get_char(){//勁者快讀 static char buf[1000001],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; }
能夠把這個模板背一下,而後就直接套本身的getchar讀入優化模板了。
固然也能夠把這裏面的static索性所有拿到外面去,變成全局變量,而後作一個宏定義:
char buf[1000001],*p1=buf,*p2=buf; #define get_char() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
雖然說這樣確定比內聯函數快,可是由於宏定義畢竟是機械式替換的……因此不保證不出玄學bug,慎用。(不過通常來講用我本身寫的模板不會有問題)
(其實我不會把只有一行語句的宏用do while(0)封裝起來,因此只能打個括號完事)
這個優化雖然快是快,可是由於奇技淫巧……不保證不出bug,因此仍是謹慎使用。
就當作普及一個玄學優化吧。
這裏有一個大神寫的整合版,大體原理是同樣的,不過代碼更簡單,並且讀入比個人又要快一倍(用的指針+一次性讀入全部的整數),代碼以下
const int MAXS = 60*1024*1024; const int MAXN = 10000000; char buf[MAXS]; int numbers[MAXN]; void analyse(char *buf,int len = MAXS) { int i; numbers[i=0]=0; for (char *p=buf;*p && p-buf<len;p++) if (*p == ' ') numbers[++i]=0; else numbers[i] = numbers[i] * 10 + *p - '0'; } void fread_analyse() { freopen("data.txt","rb",stdin); int len = fread(buf,1,MAXS,stdin); buf[len] = '\0'; analyse(buf,len); }
最後貼上我把這個玄學優化寫進個人玄學getchar裏面弄出的超級巨型玄學賊有意思奇技淫巧讀入優化
char buf[1000001],*p1=buf,*p2=buf; #define get_char() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++) #define gi(a) do { \ register char ch; \ while((ch = get_char()) > '9' || ch < '0'); \ for(a = ch-'0'; (ch = get_char()) >= '0' && ch <= '9'; a = a*10+ch-'0'); \ }while(0)