首页 > 编程语言 >【C++算法】 卡常技巧

【C++算法】 卡常技巧

时间:2024-04-02 19:30:42浏览次数:34  
标签:tmp 运算 int C++ 算法 63 long 卡常 Rightarrow

文章目录


updata

  • 2024.03.31 发布此文章

学习引言

卡常,一种编程技巧,在对时间复杂度要求很高时,就可以用这种办法来节省时间。

今天,我们也来学习一下卡常。

技巧 1——善用修饰符

在定义变量前可以加上 register 修饰符,这样在使用时可以加快速度。

在自定义函数前则可以加上 inline,这样也可以加快速度。

技巧 2——输入输出

如果把输入输出速度按从快到慢排序的话,如下:

  1. freadfwrite
  2. readwrite,此函数需自己编写,在下面会讲;
  3. scanfprintf
  4. cin.tiecout.tie
  5. cincout

看到了吧,我们常用的 cincout 是最慢的,所以呢,在卡常的时候,一定不要用 cincout,并且,即始用了 .tie 加速后其实还是和 scanfprintf 相差很远。

readwrite

此函数原理简单,众所周知,getcherputcher 函数是非常快的,但只能输入(输出)单个字符,我们其实也可以用他们一位一位的进行输入(输出),代码如下(不能进行负数操作,负数操作其实很简单,只要加个判断就可以了):

inline long long read() {
	register int x=0;register char c=getchar(),f=1;
	for(;c-48>>63||57-c>>63;c=getchar()) if(c==45)f=-1;
	for(;47-c>>63&&c-58>>63;c=getchar()) x=(x<<3)+(x<<1)+(c^48);
	return x*f;
}
inline void write(register long long x) {
	if(x-0>>63) putchar(45),x=~x+1;
	if(9-x>>63) write(x/10);
	putchar(x%10^48);
}

技巧 3——对于运算的优化

众所周知,计算机电脑内部存储数据使用的是二进制,而位运算与二进制有直接联系,故系统处理位运算时在效率上有很大优势。但是,要注意,位运算优先级很低的哦!

俗话说:没有对比没有伤害。这里列出了程序执行各种运算所需的时间周期:

运算执行一次需要的周期
加法/减法 1 1 1
乘法/除法 3 3 3
取模 20 20 20
位运算 < 1 <1 <1

由此,我们也就看出了位运算的速度之快以及取模的速度之慢。所以,我们能用位运算其实就可以用位运算。

实际上,我们很多常见的操作都可以用位运算来算,这里统计在下:

  1. 乘上一个 2 整数次幂,可以改用左移运算加速,格式为 原数字<<=幂,效率约增加 300 % 300\% 300%;
    x*=8 ⇒ \Rightarrow ⇒ x<<=3
  2. 除以一个 2 整数次幂,可以改用右移运算加速,格式为 原数字>>=幂,效率约增加 300 % 300\% 300%;
    x/=8 ⇒ \Rightarrow ⇒ x>>=3
  3. 判断一个数是否为奇/偶数,可用位运算加速,效率约增加 500 % 500\% 500%;
    x%2==0 ⇒ \Rightarrow ⇒ ~x&1
    x%2==1 ⇒ \Rightarrow ⇒ x&1
  4. 判断一个两个数是否满足 x > y x>y x>y,可用位运算加速约 200 % 200\% 200%;
    x>y ⇒ \Rightarrow ⇒ y-x>>31(注:这里的 x , y x,y x,y 都是 int 型变量,若 x , y x,y x,y 为 long long 型变量则将 31 31 31 改为 63 63 63。
  5. 快速求一个数的相反数,可用位运算加速约 400 % 400\% 400%;
    -x ⇒ \Rightarrow ⇒ ~x+1
  6. 快速求一个数的绝对值,可用位运算加速约 400 % 400\% 400%;
    x<0?-x:x ⇒ \Rightarrow ⇒ x^(~(x>>31)+1)+(x>>31)
  7. 交换两数 x , y x,y x,y,可用位运算加速约 30 % 30\% 30%;
    swap(x,y) ⇒ \Rightarrow ⇒ x^=y^=x^=y
  8. 一个数取模另一个数,可用乘除法加速约 65 % 65\% 65%;
    x%=y ⇒ \Rightarrow ⇒ x-x/y*y

技巧 4——展开循环

举个的例子,我们要对一个序列求和,我们一般这样写:

long long sum=0;
for(int i=1;i<=n;i++) sum+=a[i];

而加了循环展开一般会这样写:

long long sum0=0,sum1=0,sum2=0,sum3=0,sum4=0,sum5=0,sum6=0,sum7=0,i=1;
for(i=1;i+8<=n;i+=8) {
    sum0+=a[i];sum1+=a[i+1];sum2+=a[i+2];sum3+=a[i+3];
    sum4+=a[i+4];sum5+=a[i+5];sum6+=a[i+6];sum7+=a[i+7];
}
for(;i<=n;i++) sum0+=a[i];

那么可能就有人疑惑了,一个好端端的循环干嘛把它 癫子一般地 展开呢?

因为如果按照前一种的写法,每次进行循环 CPU 都要执行 n n n 次 i++,以及 n n n 次 sum+=a[i] 的操作。而一般来说 CPU 中是有多个运算器的,前一种写法相当于将所有的运算都抛给了一个运算器,其它运算器都在那儿睡大觉,这显然是一种资源浪费。使用循环展开就可以始 睡觉的 CPU 起床 CPU 并行,也就大大加快了程序运行的效率。

值得注意的一点是,循环展开不能展开太多,一般是展开 4 4 4~ 8 8 8 层,否则程序运行的效率反而会变慢 CPU 只有那么多,又不能分身

还有一点就是循环展开一般要用到很多结构非常相似的代码,此时就可以考虑 #define func(i)

事实证明循环展开效果比前几种卡常方式要好得多,一下子就少了 0.5 s 0.5s 0.5s:

for(int i=1;i<=k;++i) {
	register int tmp=0;
	for(register int j=i;j<=n;j+=5) {
		#define swp(j) if(a[j]>tmp&&j<=n) a[j]^=tmp^=a[j]^=tmp
		swp(j);swp(j+1);swp(j+2);swp(j+3);swp(j+4); 
	}
	a[i]=tmp;
}

当然我们还是不够满意,因为这边还有个 j<=n,每次展开都要判断一次,显然大大拖慢了程序运行的效率,此时你也可以进一步将 j<=n 展开,卡到最后的代码长这样:

for(register int i=1;i<=k;++i) {
	register int tmp(0),j(i);
	#define swp(j) (a[j]>tmp&&(a[j]^=tmp^=a[j]^=tmp))
	for(;j+5<=n;j+=5) {
		swp(j);swp(j+1);swp(j+2);swp(j+3);swp(j+4); 
	}
	(j<=n)?swp(j):0;(j+1<=n)?swp(j+1):0;(j+2<=n)?swp(j+2):0;(j+3<=n)?swp(j+3):0;(j+4<=n)?swp(j+4):0;
	a[i]=tmp;
}

技巧 5——对与循环的优化

有如下的程序:

for(int i=1;i<=k;i++) {
	int tmp=0;
	for(int j=i;j<=n;j++) {
		if(a[j]>tmp) a[j]^=tmp^=a[j]^=tmp;
	}
	a[i]=tmp;
}

据网上某些博主所说,将 i++ 改为 ++i 能提升程序效率,实测效果不明显, n = 50000 , k = 20000 n=50000,k=20000 n=50000,k=20000 的运行时间只提升了 0.01 s 0.01s 0.01s。还有就是将 int i=1 改为 int i(1) 也能加快程序运行效率,不过同样效果不太明显。

标签:tmp,运算,int,C++,算法,63,long,卡常,Rightarrow
From: https://blog.csdn.net/xzz_0611/article/details/137190867

相关文章

  • 常见面试算法题-报文解压缩
    ■ 题目描述为了提升数据传输的效率,会对传输的报文进行压缩处理。输入一个压缩后的报文,请返回它解压后的原始报文。压缩规则:n[str],表示方括号内部的str正好重复n次。注意n为正整数(0<n<=100),str只包含小写英文字母,不考虑异常情况。输入描述:输入压缩后的报文:1)不考......
  • c++蛮力法解释
    蛮力法(bruteforce)是一种基本的问题求解策略,也被称为穷举法。它的基本思想是通过穷举所有可能的解来寻找问题的解决方案。在C++中,可以使用循环和条件判断语句来实现蛮力法。下面是一个示例,假设要解决的问题是找到数组中两个数的和等于给定目标值的情况:#include<iostream>#i......
  • 自然语言处理基础知识入门(二) Word2vec模型,层次softmax,负采样算法详解
    文章目录前言一、Word2vec模型1.1什么是Word2vec模型?1.2Word2vec模型是如何训练?1.3Word2vec最简单版本整体过程1.4Word2vec详细过程1.5CBOW整体过程1.6Skip-gram整体过程二、优化算法2.1层次softmax2.1.1哈夫曼树2.1.2算法详细逻辑2.2负采样策略总结......
  • 在VS或者CLion中引入C和C++的SDK
    visualstudio创建c++项目引入头文件和库文件拷贝的gpt的,可以用在VisualStudio2022中,虽然你创建的是一个C++项目,但它确实支持C语言的编译和运行。为了在你的项目中使用C语言的头文件和库文件,你可以按照以下步骤操作:1.**添加头文件和库文件到项目:**-首先,你......
  • 优化之前的ocr算法
    importtorch.nnasnnfromcollectionsimportOrderedDictclassBidirectionalLSTM(nn.Module):def__init__(self,nIn,nHidden,nOut):super(BidirectionalLSTM,self).__init__()self.rnn=nn.LSTM(nIn,nHidden,bidirectional=True)......
  • 14天【代码随想录算法训练营34期】 第六章 二叉树part01(● 理论基础 ● 递归遍历 ●
    理论基础种类满二叉树:k是深度,node数为2^k-1完全二叉树:二叉树底部是从左向右持续的二叉搜索树:左边节点都小于中间节点,右边节点都大于中间节点平衡二叉树AVL:左边和右边高度相差不超过1存储方式链式存储:leftchildptr,rightchildptr线式存储:字符数组保存,2i+1是左孩......
  • C++ std常用math函数
    std::atan和std::atan2std::atan(x)  即tan(angle)=x  所求angle范围[-PI/2,PI/2] [-90°,90°]std::atan2(y,x)即tan(angle)=y/x 所求angle范围[-PI,PI][-180°,180°]  std::fmod(x,y)计算x/y的浮点余数,如std::fmod(3.1,2)=1.1对浮点数进行......
  • 使用支持向量机算法解决手写体识别问题
    文章目录支持向量机导入图片测试算法fromgoogle.colabimportdrivedrive.mount("/content/drive")Drivealreadymountedat/content/drive;toattempttoforciblyremount,calldrive.mount("/content/drive",force_remount=True).支持向量机fromnumpy......
  • test c++
    testc++ #include<iostream>usingnamespacestd;intmain(){charmyChar[6]={'H','e','l','l','o','\0'};//char*pointer=myChar;//WORKS!!!char*pointer......
  • 常见的排序算法——选择排序
    本文记述了选择排序的基本思想和一份参考实现代码,并在说明了算法的性能后用实验进行了验证。◆思想将第一个元素开始的所有元素作为待排序范围,通过一一比较,查找待排序范围内的最小元素,将其与范围内的第一个元素交换。然后将从第二个元素开始的所有元素作为新的待排序范围。重复......