您的位置:首页 > 其它

算法设计:如何求出一个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位中去,感兴趣的读者,可以自行进行移植。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐