Chinaunix首页 | 论坛 | 博客
  • 博客访问: 641929
  • 博文数量: 108
  • 博客积分: 46
  • 博客等级: 民兵
  • 技术积分: 1279
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-16 11:36
个人简介

坐井以观天

文章分类

全部博文(108)

文章存档

2014年(13)

2013年(90)

2012年(6)

分类: C/C++

2013-07-11 21:46:51

今儿在网上看到这样一个似曾相识的算法题,有点儿想法,觉得值得回味,马上记下来:

题目描述:对于一个字节(8bit)的无符号整形变量,求二进制表示中“1”的个数,要求算法执行效率尽可能地高。

先看看我自己的答案(方法一)
unsigned char Count(unsigned char byt)
{
unsigned char num=0;
while (byt)
{
num += (byt & 0×01);
byt >>= 1;
}
return num;
}
不管有多少个1都要循环8次,执行效率不高,但是执行该函数的时间每次都是确定的。

方法二:
直接的方法就是除以2向右移位, 逐个统计,但是用到取模和相除,这个很耗资源。
int Count(BYTE v)
{
int num=0;
while (v)
{
if (v%2==1)
{
num++;
}
v=v/2;
}
return num;
}
求余、除法很耗资源,写程序时应慎用。


方法三:
使用位操作,但是只会去统计1的个数,循环的次数是BYTE中1的个数,无需遍历。
int Count(BYTE v)
{
int num=0;
while (v)
{
v &=(v-1); //v=v&(v-1)这个操作可以直接消除掉v中的最右边的1。
num++;
}
return num;
}
循环次数与Byte中1的个数有关,但是函数执行时间不确定,不过效率比前面的要提高了很多,是不是以为这就是最佳答案了吧,告诉你:NO。


方法四:

查表法,这个的效率应该是最高的了——空间换时间。将0~255各个数中所含的1列出来,查表!!
int countTable[256]=
{
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};
int Count(BYTE v)
{
return countTable[v];
}
这个程序要求效率尽可能的高,显然最后一种的时间复杂度最低了O(1).执行时间也是确定的。空间换时间在某些情况下是个好的选择,比如需要频繁使用这个算法的时候,但也不是尽然,还是得视情况而定。
该题出自,在这儿先谢谢同仁了!!!

上面几种算法中,第三种似乎是在哪儿见过的,当我看到第三种算法后准备止步的,没想到后面还有一种算法,看后很是让我震惊!!居然是以一些人(包括我了^_^)常常在嘴边说的“以空间换时间”的原
则去解决的,但是在每次遇到这种问题究竟有没有真正的去考虑这个事儿,把应用场景与这些“猿人”们都能朗朗上口的原则放在一起仔细想想,到底有没有“更”合适的解决方案??想到这里,还是觉得自
己虽说表面上行动上很积极,可思想上有时还是比较老套,不前进!得反思自己了。。。

对于上面这个题,估计很多人在第三种方案那里就已经止步了,觉得自己做得已经足够好了,已经比第一种、第二种更前进!这些人估计都是站在“怎样以最快的速度去找出1的个数”角度去想的,却没有从
“怎么用空间上的开销去换取时间上的开销,在空间允许的情况下达到最快的效率”。

大家如果有什么相似的案例,希望能拿出来分享分享。^_^

阅读(7720) | 评论(10) | 转发(13) |
给主人留下些什么吧!~~

yajiedesign2015-06-04 09:49:56

第一个想到的算法就是查表法的怎么破...

xdsnet2013-11-20 19:51:58

yulc:32位,甚至64位,也是一样的,用空间换时间。
把32位的分解成4个8位的,余下的就一样了,再把这4个结果加起来。

确实算法很精巧啊。

回复 | 举报

joepayne2013-09-10 16:54:55

补充一个实例:
    在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输入b。这里只限
 解答:
char* findOnlyOne(char *str)

{
    assert((str != NULL));
    int help[256] = {0};
    char *tmp = str;
    //统计字符串中各个字符出现的个数
   while(*tmp)
   {
     help[*tmp]++;
     tmp++;
   }
 tmp = str;

joepayne2013-07-15 11:03:37

yulc:32位,甚至64位,也是一样的,用空间换时间。
把32位的分解成4个8位的,余下的就一样了,再把这4个结果加起来。

多谢大哥提示,小弟可不可以这么理解,大哥看看对不对:
首先,先取出目标数(假如是32位数)的第一个字节,进行上面第四种方案的计算,计下1出现的个数;
然后,再取出目标数的第二个字节,进行上面第四种方案的计算,计下1出现的个数;
。。。。。。。。。。第三个字节,。。。。。。。。。。。。。。。。。。。。。;
。。。。。。。。。。第四个字节,。。。。。。。。。。。。。。。。。。。。。;
最后,累加1出现的个数。
CPU只不过重复做了4次的计算,时间复杂度不变,空间复杂度也没有变(共用一个查找表)。
小弟见识短浅,在这里向大哥膜拜一下!嘿嘿

回复 | 举报

yulc2013-07-13 17:30:34

joepayne:兄台说的在理啊!的确应该是这样的。其实这个地方小弟只是抛砖引玉,希望在这方面有经验案例的大神们分享分享!嘿 
说的“空间换时间”,一般在当这个算法(接口)需要频繁调用且对效率又有较高要求的时候,而且在空间允许的条件下,考虑能不能用空间上的牺牲来换取效率上的突破。还是拿上面这个例子来说事儿,显而易见第三种方案与第四种方案在效率上差别是非常大的!当然了,如果如兄台所说如果扩展为32位,“表”所占的空间也将会非常庞大----预计是s_tb_size = power(2,32)*sizeof(unsigned char)个字节(即为2G内存),很明显这已经威胁到应用的栈空间了!呵呵 在这种情况下,应用真的就得慎重考虑考虑内存上的开销了!!!^_^

32位,甚至64位,也是一样的,用空间换时间。
把32位的分解成4个8位的,余下的就一样了,再把这4个结果加起来。

回复 | 举报