算法设计:如何求出一个64位数中哪一位是‘1’
2014-06-08 22:43
246 查看
问题描述
今天在写游戏的时候,有这么一个需求:我需要根据一个标示符的值,标示符的值一般都是用这样的数值来表示:
如用(1 << 2) 这个来标示一个可以被渲染的对象, (1 << 3) 来标示一个可以进行碰撞的对象,诸如此类的。
所以,我需要根据上面的标示符来获取上面标示符中哪一位为1?
也就是说,对于(1 << 2) 这个值, 它的第2位为1(从下标为0开始计算)。
算法设计
对于这样的一个算法问题,很容易想到,对每一个位使用逻辑运算来判断下,这样就能够简单的获取到哪一位为1了。对,采用这样的方法,在算法设计上,称为蛮力法。蛮力法是一个问题的最直接,最容易想到,也是最标准的做法了。但是,由于游戏的效率问题,我这个函数要频繁的调用,所以我想将它稍微的提高一下效率。
最近浏览了下《算法设计与分析基础》,大体的内容没有仔细的看,只是简单的浏览了下,设计算法时的几种有效工具,以及他们的思想。
所以,我一下就想到,使用分治法来设计这个算法。
下面算法的概要描述:
我们假设,这个标示符,使用64位无符号整形来表示,那么,我们每一次将这个值折半,也就是说,将64位分为两个部分,分别是高位部分,和低位部分,他们都是有32位组成的。然后在对这32位的值,不停的进行折半,知道最后折半成为1位的数,然后我们在来对这个1位的数判断,是否为1,这样我们就能够计算出哪一个值为1了。
(感觉上,好像和蛮力法的效果一样啊???由于对于算法复杂度分析还不是很了解,所以就暂时不管是否真的比蛮力法高效了^_^)
代码
最后给出程序的代码,很显然,使用分治法,需要用到递归,所以,我使用递归来设计这个函数,而且,读者请注意,这个算法只是针对整形数据中,只有一位为1的情况,如果有多个位为1,这种行为,程序未对其进行考虑。以下是最终的代码:unsigned int getbitone64(unsigned _int64 value, unsigned int number, int index) { if(value == 1) return index ; else if(number == 1) { return -1 ; } else { unsigned int result = -1 ; unsigned _int64 temp = value ; unsigned _int64 half_num = number/2 ; int offset = 64 - half_num - index ; temp = temp << offset ; temp = temp >> offset ; result = getbitone64(temp, half_num, index); if(result != -1) return result ; result = getbitone64(value >> half_num, half_num, index + half_num); if(result != -1) return result ; } }// end for getbitone64
很简短的一段代码。
注意点
上面的代码有几个地方需要注意,这些地方都是我写代码的时候犯错的地方。对于获取值的左半部分,和右半部分,我是采用移位的方式来进行的。获取左半部分,如0x0010 0000中的0010,这个很容易,只要将这个值右移16位就好了。所以,同样的,对于64位的值,我们需要右移32位来获取左半部分的值,对于32位的时候,我们右移16位,依次类推。对于获取右半部分,就有点混乱了。我才开始的时候想到的算法是这样的,在64位值情况下,只要将值先左移32位,然后在右移32位,就可以了。所以,我就觉得,对于上面问题中,所有的折半情况,都是先左移值的一半,然后在右移。后来发现,这个方法,只是在才开始的时候,值为64位时有效。一旦进行了折半,比如还剩下32位了,那么如果我先左移16位,再右移16位,实际上是没有用的。为什么了?读者如果细心的话,就会发现,我在上面的代码中一直都是使用64位的unsigned
_int64来保存折半后的值,也就是说,不管折半以后还剩下多少,我总是用64位值来保存。所以,比如,在折半后,最后还剩下10,这两个值,如果用上面的算法来求右半部分的话,我们先左移1位,等于100,再右移1位等于10,最后的结果不是0而是10(2)。所以这就是使用64位值来保存时,没有移出64位的范围,最高位的1没有被清0。所以,我想到,不应该总是移动一半的值,而是64 - 一半的值。还是拿10来说明,现在要移动的数据应该是64 - 1 = 63 位,左移63位得到的就是0了,然后再右移63位得到的还是0,这样就准确的获取了右半部分。
第二个问题是,我才开始设计时,没有考虑符号位的关系。所以使用的是_int64的值。但将1移到最高位的时候,它变成了符号位。我们知道,在有符号的数中,不管怎么移动,它的符号位都是保存不变的,所以这就坑了,进行判断的时候,总是是个负数。
好了,上面两点就是在设计过程中遇到的主要问题,明白了这些细节,也就不是很麻烦了。
上面的算法,很容易移植到32位,16位中去,感兴趣的读者,可以自行进行移植。
相关文章推荐
- 设计一个算法将一个线性表如何将大于0的数放在后面,小于0的放在前面。
- 机器学习6--如何衡量一个学习算法的性能和如何设计一个系统
- 如何设计一个泛型算法
- 如何设计一个高效算法从N个正整数中,随机选取n个不同的随机数 n<=N
- 如何将算法翻译成代码,软件设计实践,一个B Plus Tree算法实现(未完待续)
- 整数数组中,每个元素均出现两次,除了一个元素例外,如何找出这个元素?能否设计一个线性时间的算法,且不需要额外的存储空间?
- 正在学设计模式呵,转载一篇如何保证一个窗体的实例运行
- 如何添加一个自定义的columnstyles 到设计器中,以便在设计时直接使用他们?
- 关于工序设计中最优解的一个算法【原创】
- 算法设计——问一个算法题(算法导论的作业题) (转载自水木清华Programming)
- 如何在一个系统中设计权限控制机制(1)
- 如何获取一个int型数字的位数
- 大规模地形渲染--如何评价一个算法的优劣
- 设计模式研究:如何判断一个封装方案是好是坏
- 如何构建一个ERP系统(需求分析、系统架构、系统设计、系统编码、测试、交付程序及文文件)。
- 如何建立一个网站?规划、设计、目的、原则、宣传(一)
- 如何建立一个网站?规划、设计、目的、原则、宣传(三)
- 如何设计一个大型的AJAX应用程序
- 一类似传销的累加算法以及如何设计数据库!
- 如何精确计算出一个算法的CPU运行时间