ImageVerifierCode 换一换
格式:DOCX , 页数:28 ,大小:33.98KB ,
资源ID:6703371      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6703371.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(位运算及其对程序的优化.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

位运算及其对程序的优化.docx

1、位运算及其对程序的优化位运算及其对程序的优化常州市第一中学 戴涵俊JSOI2009省队论文目 录序言 3 正文 4一、位运算的基本操作 4 1.位运算介绍 2.位运算的优先级 3.位运算的口诀二、位运算的实用技巧 5 1.对于mod运算的优化 2.位运算的一些技术 三、位运算对一些数据结构的优化 6 1.循环队列 2.树状数组 3.集合 4.哈希表四、位运算对一些算法的优化 12 1.状态压缩动态规划 2.搜索五、总结 15六、附录 15七、参考资料 24 序 言 程序中所有的数据在计算机内存中都是以二进制的形式储存的。位运算,本质上就是直接对整数在内存中的二进制位进行运算,同时,数的各个二进

2、制位互不影响。由于位运算直接对内存数据进行操作,不需要转换成十进制,因此处理速度非常快,在信息学竞赛中往往可以优化理论时间复杂度的系数。另外,位运算还有很多特殊的技巧,能够帮助我们简化代码、美化程序等等。本文就结合自己的学习和应用经验,介绍一些位运算及其对程序的优化方法。正 文一 位运算的基本操作1. 位运算介绍 andXYX and Y000100010111 通过上表不难发现,只有当x和y都为1时and后值才为1。and运算主要是用来取出某个二进制位。例如:A and (1 shl 5)就是取出A的二进制数从右往左第6位。 orXYX and Y000101011111 or运算通常用来强

3、行给二进制的某一位赋值,注意or运算可能导致变量越界,对于有符号类型,or可能把符号位取反,无符号类型可能直接就变成0了,这有可能会导致数据的丢失,使得程序崩溃。因此,执行or运算,应该尽量保证变量不超界,或者更保险地,是非负数。3 xorXYX and Y000101011110 当两个位不同时得到1,否则为0。因此xor通常可以用来取反。有意思的是xor的逆运算是其本身。于是,我想起了一道做过的有点诡异的题目:给你n个数,n大到只保证你读入不超时,其中仅有一个数出现了奇数次,要你找出这个数。方法是只要通过读一个xor一个,出现偶数次的肯定抵消了,剩下来的就是那个数了。4 notXnot(X

4、)0110 not操作就是直接把内存中的0和1全部取反。对于有符号类型,符号位也会取反,这是需要注意的,比如x2147483647,x:longint; not(x)就得到了2147483648,即0111111是表示maxlongint,1000000是表示的maxlongint-1。5 shl 左移,就是把二进制数整体向左移动x个位,并且右数x个单位是0;如果移出界,那么移出部分就丢失了,而不会runtime error。对于有符号类型,移位当然会移到符号位上去,比如x230,x:longint;那么x shl 1就得到2147483648;6 shr 右移,就是把二进制数整体向右移动x个

5、位,原来的最高x个位就变为0;shr相当于div 2。2. 位运算的优先级not and , shl ,shr or,xor比如下面的几个运算:not 1 or 1 -1not 1 and 1 01 and 1 shl 1 2not 1 or 0 shl 1 xor 0 and 1 = -2 虽然掌握各个运算符的优先级并不困难,但是为了避免出错,增强程序的可读性,利于调试,我们还是在需要的地方添加括号来保证优先运算。3. 位运算的口诀清零取反要用与,某位置一可用或。若要取反和交换,轻轻松松用异或。4. 举例例1、一个文件中有9亿个不重复的9位整数,现在要求对这个文件进行排序(当然时间可以不止1

6、秒,但要求出可行解)。问题分析拿到题目也许会吓一跳,这么多数,就是想快排存也存不下啊。线性时间的排序算法桶排序?貌似是这样,但是9位数,这么大的哈希数组也弄不下啊。注意到每个数都不同,那么每个数顶多出现1次,也就是说,要么出现,要么不出现。那么,我们可以用0表示未出现,1表示出现。当然,这里一个longint不够,那么就109 div 32+1个longint,对于一个数,判断它是在哪一个longint的哪一位,把它变成1。输出时当作哈希表一样处理就可以了,总的时间复杂度是O(n),比爆空间且缓慢的快排、裸的桶排序等算法要好的多。二 位运算的实用技巧1. 对于mod运算的优化 一些特殊的模运算

7、可以用位运算来代替,比如模2的整数次幂,我们可以用and运算来代替mod运算,程序段如下(以mod 1048576为例, y=134328497)。 mod版本:for i:=1 to time do x:=y mod base; and 版本:for i:=1 to time do x:=y and (1 shl 20-1);当time分别取10000000,100000000,1000000000的时候,各自的用时如下:modand100000000.22s0.14s1000000001.26s0.52s100000000012.00s4.23s 上面是在我的酷睿T2370笔记本上测试的,

8、用go32v2编译执行。虽然不同电脑,不同编译器结果可能不同,但是,我们不难发现,改成and之后,效率有了较大的飞跃,而且运算次数越多,效率提升越明显。2. 位运算的一些技术 运算要求用位运算实现实际应用把右数第k位强行赋为1x or (1 shl (k-1)通过这些操作,我们可以用01表示某个状态并且方便地改变这一状态,在哈希表、状态DP、一些搜索记录状态中很实用把右数第k位强行赋为0x and not(1 shl (k-1)右数第k位取反x xor (1 shl (k-1)去掉右起第一个1的左边x and (x xor (x-1)树状数组中用到的低位技术求x和y的平均值下取整x and y

9、+(x xor y) shr 1避免x+y超界但结果不超界的情况求x的相反数(x基类型为有符整型)not x1更好地理解not以及数在计算机中的存储方式交换变量x和变量y的值x:=x xor y; y:=x xor y;x:=x xor y;省去交换变量时候需要的临时变量用位运算取绝对值(以32位整数为例)x xor (not (x shr 31) + 1) + x shr 31三 位运算对一些数据结构的优化1. 循环队列循环队列比较方便的实现可以用一个头指针head,一个尾指针tail,每次取出来的是head mod base。这里不妨把base设置为2的整数次幂1,然后用and来取模,既方

10、便了代码书写,又不会降低效率,还美化了程序。举一个例子,SPFA是大家熟悉的求最短路径的简单、高效的算法,它需要维护一个队列,并且队列中最多会有n个元素(n为顶点数)。于是,这里的队列我们可以用位运算优化的循环队列来做,代码见附录1。2. 树状数组这种数据结构可以用线段树来代替,但是线段树比较烦琐,并且理论时间复杂度系数比较大,而树状数组就比较好写,且系数比较小。关于树状数组的具体原理就不赘述了,这里讲讲它的关键技术低位技术(Lowbit),树状数组需要知道一个数的二进制表示中从右往左第一个1的位,用位运算实现只要执行:Li:= i and (i xor (i-1)。3. 集合用一个二进制数来

11、表示一个集合的状态,用来替代PASCAL中缓慢的“集合”这种数据结构,可以大大提高程序的运行效率。因为每次位运算的操作可以看作是O(1)的;同时,这种表示方法也利于编程,我们可以方便地用一个二进制数来表示。唯一的缺点就是,数感不好的同学不能一眼看出一个十进制数的某一位是1还是0。下面列举了一些集合的操作用位运算来实现的方法。功能用位运算实现集合的并、交、差A or B ; A and B ; A and not B添加/删除某一个元素A or 1 shl (k-1) ; A and not(1 shl (k-1)判断某一元素是否存在A and (1 shl (k-1) 是否为0。 为0就是不存

12、在从A集合中删去B集合(B集合为A集合的子集)A xor B 例2、PIGS问题描述:尼克在一家养猪场工作,这家养猪场共有M间锁起来的猪舍,由于猪舍的钥匙都给了客户,所以尼克没有办法打开这些猪舍,客户们从早上开始一个接一个来购买生猪,他们到达后首先用手中的钥匙打开他所能打开的全部猪舍,然后从中选取他要买的生猪,尼克可以在此期间将打开的猪舍中的猪调整到其它开着的猪舍中,每个猪舍能存放的猪的数量是没有任何限制的。买完猪后客户会将他打开的猪舍关上。好在尼克事先知道每位客户手中有哪些钥匙,要买多少猪,以及客户到来的先后次序。请你写一个程序,帮助尼克求出最多能卖出多少头生猪。输入格式:输入文件的第一行包

13、含两个整数M和N,1M1000,1N100,M为猪舍的数量,N为客户人数,猪舍的编号为1到M,客户的编号为1到N。输入文件第二行包含M个空格隔开的整数,依次表示每个猪舍中的生猪数量,每个整数大于等于0,且小于等于1000。接下来的N行每行表示一位客户的购买信息,第I个客户的购买信息位于第I+2行,其格式如下:A K1 K2KA B它表示该客户共有A把钥匙,钥匙编号依次为K1 K2KA,且K1K2KA,B为该客户要买的生猪的头数。输出格式 输出文件仅有一行包含一个整数,表示尼克最多能卖出的生猪的头数。样例:PIGS.IN3 33 1 102 1 2 22 1 3 31 2 6PIGS.OUT7问

14、题分析看到每个猪舍有数量限制、每个人有需求量上限,我们不难想到网络流的模型。首先可能会想到一个比较裸的模型,就是先一个源点向m个猪舍连边,上限为猪的数量,然后第一个客户向可以取到的猪舍连边,上限为正无穷,并且他向汇点连边,上限为需求量;第一个客户再伸出m条边重新引出m个猪舍,再作为新的源点向第二个客户连边,以此类推。这个图的层次、点数都相当多,显然,我们要进行优化。因为可以把一个猪舍的猪趁打开猪舍时候赶到另外一个猪舍去,于是两个不同的客户如果有相同的猪舍可以开,就可以共享他们所能开到的所有猪舍的猪。于是我们可以直接对两个客户连边,即,如果客户x和y,满足xy并且x和y所能开的猪舍交集不为空,那

15、么x就向y连边,这样x的猪舍的猪都能被y享用到了。问题似乎到这里就完了,其实不然。由于要对任意两个人进行判集合的交是否为空,那么复杂度是100*100,然后要对所有的猪舍进行比对,这个复杂度是1000,于是,这个预处理的理论复杂度上限可能是O(108),虽然经试验下来不太可能达到这个复杂度,但是我们还是要用一种简单易行的方法进行优化位运算。这里用整数来表示猪舍的有无情况,这样最大可以有1000个二进制位,于是还要分段处理。不妨每段用一个64位的qword,把1000总共分成16段,对于每个猪舍编号k,先判断是哪一段的(用(k-1)div 64+1,可以写成shr 6加速),然后再在那一段进行赋

16、值。于是,对于两段猪舍,看有无交集,只需要and一下,看是不是0就可以了。这样,我们把预处理的时间复杂度降到了O(100*100*16)。虽然本题用位运算做有点显得多余了,但是这种方法还是有推广意义的。例3、山贼集团问题描述:某山贼集团在绿荫村拥有强大的势力,整个绿荫村由N个连通的小村落组成,并且保证对于每两个小村落有且仅有一条简单路径相连。小村落用阿拉伯数字编号为1,2,3,4,n,山贼集团的总部设在编号为1的小村落中。山贼集团除了老大坐镇总部以外,其他的P个部门希望在村落的其他地方建立分部。P个分部可以在同一个小村落中建设,也可以分别建设在不同的小村落中。每个分部到总部的路径称为这个部门的

17、管辖范围,于是这P个分部的管辖范围可能重叠,或者完全相同。在不同的村落建设不同的分部需要花费不同的费用。每个部门可能对他的管辖范围内的小村落收取保护费,但是不同的分部如果对同一小村落同时收取保护费,他们之间可能发生矛盾,从而损失一部分的利益,他们也可能相互合作,从而获取更多的利益。现在请你编写一个程序,确定P个分部的位置,使得山贼集团能够获得最大的收益。输入格式: 输入文件第一行包含一个整数N和P,表示绿荫村小村落的数量以及山贼集团的部门数量。 接下来N-1行每行包含两个整数X和Y,表示编号为X的村落与编号为Y的村落之间有一条道路相连。(1=X,Y=N) 接下来N行,每行P个正整数,第i行第j

18、个数表示在第i个村落建设第j个部门的分部的花费Aij。 然后有一个正整数T,表示下面有T行关于山贼集团的分部门相互影响的代价。(0=T=2p) 最后有T行,每行最开始有一个数V,如果V为正,表示会获得额外的收益,如果V为负,则表示会损失一定的收益。然后有一个正整数C,表示本描述涉及的分部的数量,接下来有C个数,Xi,为分部门的编号(Xi不能相同)。表示如果C个分部Xi同时管辖某个小村落(可能同时存在其他分部也管辖这个小村落),可能获得的额外收益或者损失的收益为的|V|。T行中可能存在一些相同的Xi集合,表示同时存在几种收益或者损失。输出格式: 输出文件一行包含一个数Ans,表示山贼集团设置所有

19、分部后能够获得的最大收益。样例数据:输入样例输出样例2 11 22113 1 15数据规模: 对于40%的数据,1=P=6。 对于100%的数据,1=N=100,1=P0 do Begin A:=(A-1) and B; End;A1的意思就是把A集合中最后一个1变成0,最后一个1后面的0都变成1(最后是指右边),然后再and一个B,保证是原来A的子集。这样做就相当方便了。4. 哈希表哈希表最关键的莫过于哈希函数了,一个好的哈希函数可以大大降低哈希过程中的冲突,一般的哈希函数就是mod一个大质数。其实,一些用位运算写的哈希函数能够获得不错的效果,华丽的位运算也体现了它独特的魅力。下面就介绍几个

20、优秀的字符串哈希函数。/DJB Hashfunction DJBHash(s:string):un;var hash:int64; i:longint;begin hash=5381; for i:=1 to length(s) do hash:=hash+(hash5)+ord(si); DJBHash:=hash and baseend;/AP Hashfunction APHash(s:string):un;var hash:int64; i:longint;begin hash:=0; for i:=1 to length(s) do if odd(i) then hash:=hash

21、 xor (hash3) else hash:=hash xor not (hash5); APHash:=hash and baseend;/SDBMHashfunction SDBHash(s:string):un;begin hash:=0; for i:=1 to length(s) do hash:=ord(stri)+(hash6)+(hash16)-hash; / equivalent to : hash = 65599*hash+ord(stri) SDBHash:=hash and baseend;/JS Hashfunction JSHash(s:string):un;be

22、gin hash:=1315423911; for i:=1 to length(s) do hash:=hash xor (hash2); JSHash:=hash and baseend;上面的哈希函数原理就不再详细阐述了,重要的是运用。用上述哈希函数来优化哈希表,可以使你的程序得到进一步优化华丽中彰显实用。例4、cryptcow(USACO 4.1)问题描述:农民Brown和John的牛们计划协同逃出它们各自的农场。它们设计了一种加密方法用来保护它们的通讯不被他人知道。 如果一头牛有信息要加密,比如International Olympiad in Informatics,它会随机地把C

23、,O,W三个字母插到到信息中(其中C在O前面,O在W前面),然后它把C与O之间的文字和 O与W之间的文字的位置换过来。这里是两个例子: International Olympiad in Informatics- CnOIWternational Olympiad in InformaticsInternational Olympiad in Informatics- International Cin InformaticsOOlympiad W为了使解密更复杂,牛们会在一条消息里多次采用这个加密方法(把上次加密的结果再进行加密)。一天夜里,John的牛们收到了一条经过多次加密的信息。请你写

24、一个程序判断它是不是这条信息经过加密(或没有加密)而得到的: Begin the Escape execution at the Break of Dawn输入格式:一行,不超过75个字符的加密过的信息。 Begin the EscCution at the BreOape execWak of Dawn输出格式:一行,两个整数. 如果能解密成上面那条逃跑的信息,第一个整数应当为1,否则为0;如果第一个数为1,则第二个数表示此信息被加密的次数,否则第二个数为0。 1 1问题分析 本题是比较明显的搜索题,但是,如果不加一点点优化是无法在有效时间里面出解的。其中之一的优化便是哈希判重了,即,已经搜

25、索过的状态就没有必要再搜索了。对于字符串的哈希我们不妨使用上面几个强大的哈希函数来进行。其余的就不多说了。四. 位运算对一些算法的优化1. 状态压缩动态规划这里的状态是可以用集合来表示的状态,其实上面一个例子已经讲到用位运算来做状态压缩动态规划了,下面再举一个例子。例5、BOND【问题描述】所有人知道秘密特务007,詹姆斯邦德,但是很少人知道,许多时候他并不亲自去做任务,而是让他的表兄弟们完成。现在每当詹姆斯收到任务,他就将任务分发给大家,于是他需要你的帮助。詹姆斯每月收到一张任务单。对于每一个任务,他都根据以往的经验,计算出他和其他几位兄弟完成的成功概率。你的程序应当找到一种分配方案,使得所

26、有任务都被成功完成的概率最大。注:所有任务都被成功完成的概率,等于每个任务都被成功完成的概率之积。【输入格式】第一行包含一个整数N(=20),表示有N个任务,而他有N个兄弟来替他完成。接下来包含N行,每行包含N个0到100之间的整数。第i行第j列的数,表示第i个任务被第j个兄弟完成的成功概率。概率以百分比的形式给出。【输出格式】仅一行,输出所有任务都被成功完成的最大概率,要求误差不超过10-6。【测试样例】bond.in 2100 10050 50bond.out50.000000【问题分析】本题还是比较明显的状态压缩动态规划,n=20。我们可以按照任务被完成情况来划分状态,fi,j表示完成到

27、第i个任务为止,人员使用情况为j的时候所能获得的最大概率。显然,这个状态划分满足最优子结构和独立性。对于状态j,我们可以方便地用每个二进制位上的数是0还是1来表示某一个人有没有被用。于是状态转移方程如下:Fi,j:=maxfi-1,k*ai,r | k or (1 shl (r-1)=j且k and (1 shl (r-1)=0;我们发现这个方程如果直接拿递推写还有点麻烦,于是想到用队列优化,这样不仅可以方便程序实现,还剔除了一些冗余状态。 实际测试下来还超时一个点,标准算法是用费用流或者km写的,这里就不再展开讨论。 2. 搜索对于搜索的优化,可以有多个方面,同样,位运算可以从多个方面对搜索进行优化。(1)状态表示下文例题中的TV一题,便是通过用二进制数结合位运算来表示状态并进行状态转移,这样不仅提高了搜索效率,而且还方便了程序实现。(2)状态判重搜索常常需要开一个哈希表来记录状态是否达到或者该状态的最优值,有了位运算,一方面,我们将状态表示成整数,通过直接寻址或者拉链储存状态;另外一方面,加入位运算的哈希函数更加强大,减少哈希过程中的冲突。例6、TV源程序名 TV.? (PAS,BAS,C,CPP)可执行文件名 TV.EXE输入文件名 TV.IN输出文件名 TV.OUT运行时间限制 3S问题描述

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1