常数优化

虽然大部分的题都不会卡常数,但是万一卡常还是很恶心的,让我们一起来看看常数优化吧~   (蒟蒻太蒟了,可能无法覆盖到所有常数优化,请见谅~)



register&inline

CPU有高速缓存,那个速度非常快,但占用内存小,加上这register后,这个变量的存放位置就在register高速缓存里。一般用于频繁修改的变量(如循环中的变量)

如:

for(register int i++;i<=100;++i) cout<<i<<" ";

在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。

其实,inline在内部的工作就是在每个for循环的内部任何调用inline函数的地方都换成了函数内部的内容

inline工作方式的影响,inline 的定义是有限制的,比如,内部有复杂语句如循环语句,或者是递归函数都不适合用inline 。

那万一不小心把递归函数调用成了inline,岂不就炸了,那为什么有时不会炸呢?

因为inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。

这里有一篇对inline的解释,写得很详细,本文以上内容摘抄了一部分此博文内容

变量的速度

在C++的运算中,bool,char都要转int运算,而bool是比char要慢的(恐怖)。

而且int是最快的运算类型。

所以说,让我们来看一看各个变量的运行速度。

先贴测试用的代码:

 1 #include <bits/stdc++.h>
 2 #include <windows.h>
 3 using namespace std;
 4 int main(){
 5     bool boolnum=0;
 6     char charnum=100;
 7     int integer=0;
 8     short shortnum=1;
 9     unsigned uninteger=2;
10     SYSTEMTIME sys_time;
11     GetLocalTime( &sys_time );
12     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
13     for(register int i=1;i<=1000000000;++i) boolnum=-boolnum+integer;//1
14
15     GetLocalTime( &sys_time );
16     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
17     for(register int i=1;i<=1000000000;++i) charnum=charnum+shortnum-1;
18
19     GetLocalTime( &sys_time );
20     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
21     for(register int i=1;i<=1000000000;++i) integer=-integer+shortnum+5;
22
23     GetLocalTime( &sys_time );
24     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
25     for(register int i=1;i<=1000000000;++i) shortnum=-shortnum+6;
26
27     GetLocalTime( &sys_time );
28     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
29     for(register int i=1;i<=1000000000;++i) uninteger=uninteger+6-shortnum;//5
30
31     unsigned short unshortint=12;
32     long long longinteger=100;
33     unsigned long long unlongint=6;
34     float fnum=0.0299;
35     double doublefnum=0.001445;
36     long double lldnum=0.0008743;
37
38     GetLocalTime( &sys_time );
39     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
40     for(register int i=1;i<=1000000000;++i) unshortint=boolnum+integer-unshortint;
41
42     GetLocalTime( &sys_time );
43     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
44     for(register int i=1;i<=1000000000;++i) longinteger=-longinteger+integer-1;
45
46     GetLocalTime( &sys_time );
47     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
48     for(register int i=1;i<=1000000000;++i) unlongint=unlongint+longinteger;//8
49
50     GetLocalTime( &sys_time );
51     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
52     for(register int i=1;i<=1000000000;++i) fnum=-fnum+lldnum;
53
54     GetLocalTime( &sys_time );
55     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
56     for(register int i=1;i<=1000000000;++i) doublefnum=-fnum+doublefnum+lldnum;
57
58     GetLocalTime( &sys_time );
59     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
60     for(register int i=1;i<=1000000000;++i) lldnum=-lldnum+doublefnum+0.001;
61
62     GetLocalTime( &sys_time );
63     printf( "%02d:%02d.%03d\n",sys_time.wMinute, sys_time.wSecond,sys_time.wMilliseconds);
64 }

很恐怖,有木有?我们来看看运行结果:

 1 29:26.013
 2 29:28.336
 3 29:30.634
 4 29:32.951
 5 29:35.079
 6 29:37.373
 7 29:39.112
 8 29:41.693
 9 29:44.027
10 29:47.182
11 29:50.909
12 29:55.212

用第(i-1)行的数减第i行的数,分别指的是:

bool,char,int,short,unsigned int,unsigned short,long long,unsigned long long,float,double,long double

的运行速度。

所以,运行2,000,000,000(二十亿)次的速度:

2.323s;
2.298s;    //char在程序中的运行难度要高于bool但仍比bool快!
2.317s;    //但我还不太明白为什么int会有点慢
2.218s;    //short和int差不多
2.294s;    //unsigned加上运算速度差不多
1.739s;    //为什么unsigned short 会这么快?
2.581s;    //long long 确实要略慢一些
2.334s;
3.155s;    //果然,float变慢了许多。
3.727s;    //double 与 float 差不多
4.303s.    // long double 的确是慢。

自增

在C++循环语句中,我们经常见到这样的语句:

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

但是i++,++i 除了调用值不同外,还有什么不同呢?

++i比i++快!   (恐怖吧)

所以我打的循环总是这样的:

for(register int i=1;i<=n;++i)

它的速度比前者快3倍以上

运算符

我们假设位运算的时间复杂度为O(1),

那么加法时间复杂度为O(5~10) 减法略高;乘法时间复杂度O(10~20),除法时间复杂度O(20~30)而%运算时间复杂度高达O(100多)!而且,mod的数越大,就越慢!!!(所以说如果一道卡常的hash,最好还是常优一下)

所以我们可以各种恶心:

1 乘二方&除以二方各种左移右移
2 乘二后按位与就是*2+1
3 %2直接用&1

还有

if()else语句比()?():()语句要慢;逗号运算符比分号运算符要快

还有MOD运算直接  a%b 用 a=a-(int)(a/b)*b; 代替,这样可以快一倍以上,但这种优化可能会挂。

结构体访问

结构体访问这东西有点神奇~

比如你:

开数组:a[10001],b[10001],n[10001];
调用时连续调用a[i],b[i],c[i]慢的一批。
开一个结构体:
struct rec{
    int a,b,n;
}r[10001];
调用时r[i].a,r[i].b,r[i].n要快一些

读入/输出优化

读入,输出优化,这个也有些恶心。一般大家直接写个read()/write()函数吧,里面用getchar()和putchar();

温馨提醒一下:一般这样就够了,但fread和fwrite会更快~

数组压维

在C++中即使你开了多维数组,在内存中仍然是线性存储的。

所以你用二维数组调用时,编译器找到这个位置要很久,不断调用更恶心!!!

但是一位数组调用会简单一些,所以说当你开一位数组时,效率会高一些。

所以你可以:

在调用a[i][j]时,用a[i*j]调用。
定义int a[m+1][n+1]时,用int a[m*n+1]定义。
甚至这样还更省空间一些!!!

循环展开

循环展开,这东西在DFS中很有用。

首先举个输入时的栗子:

1 for(register int i=1;i<=n;i+=5) cin>>a[i]>>a[i+1]>>a[i+2]>>a[i+3]>>a[i+4]>>a[i+5];
2 这样会快很多~

在矩阵中DFS时,经常这么写:

int px[5]={0,1,0,-1,0};
int py[5]={0,0,-1,0,1};
void search(...){
    for(register int i=1;i<=4;++i){
        ......
    }
}

殊不知,这样很慢,既费空间,又费时间。
所以可以这么打:

void search(int nx,int ny,...){
    ......
    ......
    search(nx+1,ny);
    search(nx-1,ny);
    search(nx,ny+1);
    search(nx,ny-1);
}

DFS时的技巧

循环展开上面提过了。

所以DFS通常有两种:剪枝和判定前移

一棵完整的搜索树是非常累赘的,通常有许多许多节点。而且每层节点都是指数级别的增加,如果能在尽可能早的地方去掉这些无用分支,搜索的性能就会有大幅度的提升。

剪枝,这种方法极常见,常见的剪枝有:

最优化剪枝
可行性剪枝
运用数学技巧的剪枝
根据题目性质的剪枝
底层优化
面向数据剪枝

最优化剪枝指当前分支算出的答案是绝对的最优答案,就不用搜索了。

可行性剪枝指当前分支的情况无论如何选择最优答案,都不会再更新答案,可以不用继续搜索了

面向数据剪枝是最有效的剪枝(比如打表......)

所谓的判定前移,其实只是底层优化的一种,就是将结束搜索的判定从下一层挪到这一层来,可以大幅减少时间消耗。

常数优化这种东西是非常多的,一般掌握几种就好了。

原文地址:https://www.cnblogs.com/oierwa-Luogu/p/10323959.html

时间: 2024-10-29 11:14:17

常数优化的相关文章

[黑科技]常数优化的一些技巧

感谢wys和小火车普及这些技巧qwq 这篇文章大概没什么营养 我们来看一道十分简单的题目: 设n=131072,输入两个长度为n的数列和,要求输出一个长度为n的数列. 其中,. 首先我们来讲讲这题怎么做. 如果数据是随机的,那么有一种神奇的做法:在a和b中分别挑出最大的p个元素,对于每个i暴力枚举每个p进行更新,这样的复杂度是O(np)的,正确性我不会分析= = 那么数据不是随机的...那么估计没有什么快速的算法,不如暴力! 以下的运行时间均为在我的渣渣笔记本中测试得到,仅供参考.测试环境Ubu

bubblesort的常数优化

内容来自TsinghuaX: 30240184X 数据结构(2015秋)这门课的Vector一章,对bubblesort有两次常数优化. 函数原型是这样的: void bubble(Rank lo, Rank hi); void bubbleSort(Rank lo, Rank hi); Rank即向量元素的秩,在此被定义为int型.lo和hi为待排序区间的左.右界桩. 外部通过调用 v.bubbleSort(lo, hi) 来给向量v的区间[lo,hi)排序,而bubbleSort调用bubb

NYOJ 654 喜欢玩warcraft的ltl (01背包常数优化)

[题目链接]:click here~~ 一个常数优化 前面的伪代码中有 for v=V..1,可以将这个循环的下限进行改进. 由于只需要最后f[v]的值,倒推前一个物品,其实只要知道f[v-w[n]]即可.以此类推,对以第j个背包,其实只需要知道到f[v-sum{w[j..n]}]即可,即代码中的 for i=1..N     for v=V..0 可以改成 for i=1..n     bound=max{V-sum{w[i..n]},c[i]}     for v=V..bound 这对于V

spoj 4487. Can you answer these queries VI splay 常数优化

4487. Can you answer these queries VI Problem code: GSS6 Given a sequence A of N (N <= 100000) integers, you have to apply Q (Q <= 100000) operations: Insert, delete, replace an element, find the maximum contiguous(non empty) sum in a given interval

常数优化技巧

今天让我们整理一下一些常数优化技巧: 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.

做OI题时的一些常用的常数优化小技巧

注意:本文所介绍的优化并不是算法上的优化,那个就非常复杂了,不同题目有不同的优化.笔者要说的只是一些实用的常数优化小技巧,很简单,虽然效果可能不那么明显,但在对时间复杂度要求十分苛刻的时候,这些小的优化对于帮助你成功卡常也是十分重要的.那么我们让切入正题吧. (1)inline放在自定义函数前 不要问为什么,加就行了!额,这个东西好像可以让你的函数有机会被计算机执行得稍微快一点,一般放在使用次数比较多的函数前,像check(),为sort()定制的CMP()等等,当然主函数前就不要放了...比如

HDU 5358(2015多校联合训练赛1006) First One (区间合并+常数优化)

HDU 5358 题意: 求∑?i=1?n??∑?j=i?n??(?log?2??S(i,j)?+1)?(i+j). 思路: S(i,j) < 10^10 < 2^34,所以log2(S)+1的值只可能在1~35之间.由于log变化缓慢的函数特性,我们可以S(i,n)分作多个相同log值的区间来计算,用pos[i][j]预处理记录每个以i为起点,log(s)值为j的区间的右边界,即可优化至nlogn的复杂度. 主要是写起来比较难一些,一些细节比较纠结,一定思路理清后再写. ps.此题卡常数毫无

bzoj 3240: [Noi2013]矩阵游戏 矩阵乘法+十进制快速幂+常数优化

3240: [Noi2013]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 613  Solved: 256[Submit][Status] Description 婷婷是个喜欢矩阵的小朋友,有一天她想用电脑生成一个巨大的n行m列的矩阵(你不用担心她如何存储).她生成的这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的元素,则F[i][j]满足下面的递推式: F[1][1]=1F[i,j]=a*F[i][j-1]+

常数优化那点事

(被各位神犇的代码所虐,故设此文) (纯经验之谈,如能指出其原理或谬误,欢迎留言) 测试环境: OS:Linux Ubuntu 14.04LTS Memory:8GB Processor:Intel® Core™ i7-4500U CPU @ 1.80GHz × 4 Compiler:GNU G++ 4.8.4(switch on -O3 optimization in release mode) Part I:输入(若无特殊说明,输入类型为int,scanf每执行一次只接收一个参数) C++