常數優化技巧

 今天讓咱們整理一下一些常數優化技巧:c++

1. 讀入優化:數組

#define sight(c) ('0'<=c&&c<='9')
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
//使用方法 read(x);

這是一直基於getchar的快速讀入。相比你們都會,不說了。函數

2.更快的讀入優化:測試

#define getchar nc
#define sight(c) ('0'<=c&&c<='9')
inline char nc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}

咱們用buf數組把全部的輸入都讀入到buf數組裏,還要快。(此後便不能用scanf和cin了,由於輸入在buf數組裏了)優化

3.若是咱們大抵知道數據輸入規模,咱們能夠這樣寫nc函數:this

#define nc *p1++
char *p1=buf
fread(stdin,buf,1,100000);//主程序里加這句話 

4.同理,咱們有輸出優化:spa

void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); }

要用的時候調用writeln。(在寫write函數的時候要少寫判斷。)操作系統

5.不要信仰STL的常數(sort除外)。調試

#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define sight(c) ('0'<=c&&c<='9')
#define swap(a,b) a^=b,b^=a,a^=b

必要的時候必定要手寫(尤爲是bitset,queue,stack)。code

such as bitset。

struct bitsets{
    long long t[4];
    void set(int x){
        int aa,bb;
        aa=x/60;bb=x%60;
        this->t[aa]|=1LL<<bb;
    }
    void reset(){
        this->t[0]=0;
        this->t[1]=0;
        this->t[2]=0;
        this->t[3]=0;
    }
    void ad(const bitsets &g){
        this->t[0]&=g.t[0];
        this->t[1]&=g.t[1];
        this->t[2]&=g.t[2];
        this->t[3]&=g.t[3];
    }
    void oo(const bitsets &g){
        this->t[0]|=g.t[0];
        this->t[1]|=g.t[1];
        this->t[2]|=g.t[2];
        this->t[3]|=g.t[3];
    }
    void xo(const bitsets &g){
        this->t[0]^=g.t[0];
        this->t[1]^=g.t[1];
        this->t[2]^=g.t[2];
        this->t[3]^=g.t[3];
    }
    bool tr(const bitsets &g){
        bool top=true;
        top&=((this->t[0]&g.t[0])==0);
        top&=((this->t[1]&g.t[1])==0);
        top&=((this->t[2]&g.t[2])==0);
        top&=((this->t[3]&g.t[3])==0);
        return top;
    }
};

6.大規模的函數參數調用最好引用(&)

好比這樣:

void X(int &x){
}

7.咱們要學會手開O2

#define MARICLE __attribute__((optimize("-O2")))

那麼咱們就能夠在要開O2的函數過程前加 MARICLE 。

或者在宏中定義如下宏

#pragma GCC optimize("-O2")

8.Ox優化並非越高越好:

 咱們發現Ox類的優化是有代價的。咱們發現開着O類優化咱們沒法調試。

-O1 提供基礎級別的優化

-O2提供更加高級的代碼優化,會佔用更長的編譯時間

-O3提供最高級的代碼優化

此外咱們發現O3優化是以縮小棧空間爲代價的,因此有遞歸的程序O2優化就夠了,O3會更慢。

9.循環優化:

for (i=1;i<=b;i++)

這樣寫是很慢的。

for (int i=1;i<=b;i++)

這樣寫要快那麼一丟丟,由於在循環體裏面定義,編譯器會把變量放到寄存器裏,這樣會更快。

for (int i=b;i;i--) //i從b到1
for (int i=b;~i;i--)//i從b到0

這樣子更快。由於是位運算。

10.枚舉子集。

 for (int i=0;i<siz;i++)
  for (int j=i;j;j=(j-1)&i)
   f[i]=.....

這樣子寫是O(3^n)的,比常規枚舉要快。

11.相同語句下,從速度上講,宏>重載運算符>函數。

12.沒有遞歸的程序前能夠加inline,這樣更快。

inline void X(int &x){
}

 

就是這樣子。

13.咱們能夠這樣子用位運算來加速邏輯判斷:

if (a^b) 等價於 if (a!=b)
if (!(a^b)) 等價於 if (a==b)

 最後聲明,其實在Os優化中,這些優化差很少都有,其實然並軟。

 好吧,我知道我寫的很沒有養分。仍是舉個栗子吧。

{如下測試都在一臺配置以下的機子中:

      CPU: intel  i3-6100 3.7GHZ

      RAM: 4.00GB

      64 位操做系統

}

  隨便寫了個程序 :

#include<bits/stdc++.h>
using namespace std;
int n,a[300007],ans;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
signed main () {
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
     for (int j=i+1;j<=n;j++)
      ans=max(ans,gcd(a[i],a[j]));
    printf("%d\n",ans);
}

而後造了一個2W數據規模的數據。 運行了38.85s.

在其上方加入如下指令:

  #pragma GCC optimize("-O2")   28.48s

在編譯器中加入O2優化指令: 28.22s

//如下操做都在O2的基礎上

    在gcd函數前加入inline 指令  28.5s (大概是系統忽視了inline ,由於遞歸的函數加inline講道理會很假)

     咱們強制內聯 :加入 __attribute__((always_inline)) 28.49s。

  把遞歸改爲迭代:

#pragma GCC optimize("-O2")
#include<bits/stdc++.h>
using namespace std;
int n,a[300007],ans,t;
int gcd(int x,int y){
    while (y) {
        t=x; x=y; y=t%x;
    }
    return x;
}
signed main () {
    freopen("a.in","r",stdin);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
     for (int j=i+1;j<=n;j++)
      ans=max(ans,gcd(a[i],a[j]));
    printf("%d\n",ans);
}

結果並無快多少,應該是O2自動把gcd展開了吧。

#pragma GCC optimize("-O2")
#include<bits/stdc++.h>
using namespace std;
int n,a[300007],ans,t;
#define getchar nc
#define sight(x) ('0'<=x&&x<='9')
inline char nc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);}
inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); }
inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); }
inline int gcd(int x,int y){
    while (y) {
        t=x; x=y; y=t%x;
    }
    return x;
}
int p[13],ed,c[300007],pppp;
signed main () {
    freopen("a.in","r",stdin);
    read(n);
    for (int i=1;i<=n;i++) read(a[i]);
    for (int i=n;i;--i) {
       ed=8;
     for (int j=1;ed<i;j+=8,ed+=8){
         p[0]=gcd(a[i-j],a[j]);    c[j]>p[0]?:c[j]=p[0];
         p[1]=gcd(a[i-j-1],a[j+1]);  c[j+1]>p[1]?:c[j+1]=p[1];
         p[2]=gcd(a[i-j-2],a[j+2]);  c[j+2]>p[2]?:c[j+2]=p[2];
         p[3]=gcd(a[i-j-3],a[j+3]);  c[j+3]>p[3]?:c[j+3]=p[3];
         p[4]=gcd(a[i-j-4],a[j+4]);  c[j+4]>p[4]?:c[j+4]=p[4];
         p[5]=gcd(a[i-j-5],a[j+5]);  c[j+5]>p[5]?:c[j+5]=p[5];
         p[6]=gcd(a[i-j-6],a[j+6]);  c[j+6]>p[6]?:c[j+6]=p[6];
         p[7]=gcd(a[i-j-7],a[j+7]);  c[j+7]>p[7]?:c[j+7]=p[7];
      } 
      for (int j=ed-7;j<i;++j){
        pppp=gcd(a[i],a[j]);    c[j]>pppp?:c[j]=pppp;}
    }
    for (int i=n;i;--i) ans>c[i]?:ans=c[i];
    printf("%d\n",ans);
}

循環展開,但不知道爲何,並無快。

應該是gcd太大了吧,反正循環展開就是這樣的。

相關文章
相關標籤/搜索