使用分治法得到一个数中位元为1的个数

有这么一个问题, 给定一个数(假定32位), 如何得到这个数转为二进制后1的个数?

解:

X=(x & 0x55555555)+((x>>1)&0x55555555)

X=(x & 0x33333333)+((x>>2)&0x33333333)

X=(x & 0x0F0F0F0F)+((x>>4)& 0x0F0F0F0F)

X=(x & 0x0000FFFF)+((x>>1)& 0x0000FFFF)

原理图:

 将原始问题(统计32个位元中1的个数),分解为统计16个位元中1的个数,在分解为统计8个位元中1的个数……, 最后统计两个位元中1的个数。最后求和。

分析:

首先请看左上角:

这两个位元中只有一个位元为1,于是将结果填写到下面的格子中。这一行以此类推。

接下来看第二行第三行:

红色表示我的上面两个位元中只有一个位元为1,青黄色表示我的上面有两个位元为1,将两个数相加得到0011,代表我上面的4个位元中有3个位元为1.

.

.

.

以此类推, 得到最后的数为 0B010111 = 23

那么现在的问题是, 如何统计1的个数然后相加呢?

首先第一行, 0x55555555 = 0B01010101010101010101010101010101

与x与的结果就将偶数位的1取了出来, x>>1与0x55555555相与,就将奇数位的1取了出来。这两个数相加, 就将每两位中的1的个数取了出来,放到了下一行的对应的位中。

请注意,由于只右移了一位,因此只是统计每两位中的1的个数 。 可以使用1111&0101测试,很容易发现问题。

0x33333333 = 00110011001100110011001100110011

与x与的结果就所有第2^n和(2^n)-1的数取出来。与x>>2相与,就是将剩余的数取出来。,这些数就是位元为1个数的和。同样可以使用 1111&0011测试。

.

.

.

.

依次类推,最终得到的就是位元为1的个数。

统计值为1的位元数有很多方法,这里只是一种较为简单的分治法。还有很多精妙绝伦的方法。

参考文献:

<<算法心得(Hacker’s Delight)>>                      --机械工业出版社

原文地址:https://www.cnblogs.com/syyxy/p/9893204.html

时间: 2024-11-05 14:59:39

使用分治法得到一个数中位元为1的个数的相关文章

面试题-10亿个数中找出最大的10000个数(top K问题)

一个较好的方法:先拿出10000个建立小根堆,对于剩下的元素,如果大于堆顶元素的值,删除堆顶元素,再进行插入操作,否则直接跳过,这样知道所有元素遍历完,堆中的10000个就是最大的10000个.时间复杂度: m + (n-1)logm = O(nlogm) 优化的方法:可以把所有10亿个数据分组存放,比如分别放在1000个文件中(如果是字符串hash(x)%M).对每个文件,建立大小为10000的小根堆,然后按有序数组的合并合并起来,取出最大的10000个即是答案. top K问题 在大规模数据

分治法求一个数组中最大最小值

分治法:将一个复杂的一分为二,然后对这两部分递归调用该函数,直到找到函数出口,求解出最简单的情况 需要注意的是分治时开始和结束位置参数的选择,一开始写的是s到mid-1,另一个是mid到e,然后就会数组为奇数个时结果对,为偶数个时结果错,后面改为s到mid,另一个是mid+1到e 结果就对了. #include<iostream> using namespace std; #define N 10 #define MAX(a,b)(a>b?a:b) #define MIN(a,b)(a&

分治法解决寻找数组中最大最小值的问题

输入: 数组A[i,…,j] 输出:数组A[i,…,j]中的max和min 1. If  j-i+1 =1   Then 输出A[i],A[i],算法结束 2. If  j-i+1 =2   Then 3.      If  A[i]< A[j]  Then输出A[i],A[j];算法结束 4. k<--(j-i+1)/2 5. m1,M1<--MaxMin(A[i:k]); 6. m2,M2 <--MaxMin(A[k+1:j]); 7. m <--min(m1,m2);

3.分治法研究-搜索数组中的最长连续递增子集

//分治算法研究 搜索数组中的最长连续递增子集var cc=consolefunction find_max_crossing_lenarray(A,low,mid,high){    var max_left=mid,max_right=mid    var left_sum=1    var sum=0    for(var i=mid;i>low;i--){        sum=A[i]-A[i-1]        if(sum==1){            left_sum++   

分治法 求 逆序对数 的个数 时间复杂度为O(n*logn)

思路: 分治法 归并排序的过程中,有一步是从左右两个数组中,每次都取出小的那个元素放到tmp[]数组中 右边的数组其实就是原数组中位于右侧的元素.当不取左侧的元素而取右侧的元素时,说明左侧剩下的元素均比右侧的第一个元素大,即均能构成一个逆序对.假设现在左侧剩余n个元素,则逆序对数+n. 另外,如果当所有右侧的元素都取完,但是左侧仍然有元素剩余时,左侧剩余的元素已经在之前的运算中加到了逆序对中,不需要再添加一次 下面给出 归并排序 和 求逆序对数 两份代码: code1: 归并排序 #includ

【分治法】最接近点对问题(转)

转自:http://blog.csdn.net/liufeng_king/article/details/8484284 问题场景:在应用中,常用诸如点.圆等简单的几何对象代表现实世界中的实体.在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息.例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点.这类问题是计算几何学中研究的基本问题之一. 问题描述:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,

利用最小堆找出10亿个数中最大的10000个数

最小堆 最小堆是一种完全二叉树,特点是根节点比两个子节点都小(或者根节点比子节点都大) 过程 先找10000个数构建最小堆 依次遍历10亿个数,如果比最小堆的最小值大,则替换这个最小值,并重新构建最小堆 最后输入10000个值 时间复杂度 构建最小堆的复杂度为 logn,求出最大m个数会构建m次最小堆,时间复杂度为 m logm, 这里m为10000 进行n次时间复杂度为n,这里n为10亿,总时间复杂度为 n m logm,即10亿 10000 log 10000 其他算法 将十亿个数排序,找出

查找最大和次大元素(JAVA版)(分治法)

问题描述:对于给定的含有n个元素的无序序列,求这个序列中最大和次大的两个不同元素. 问题求解分析(分治法):先给出无序序列数组a[low...high].第一种情况为当数组中只有一个元素时,此时只存在一个最大值即为本身,次大值为负无穷,在这里我设置为-999999,第二种情况为数组中只有两个元素,此时最大值和次大值很显然将两个元素比较即可.第三种情况为数组中的元素大于两个,此时用分治法,将数组中元素砍为两半,像我们将香肠折半,注意的是此时中间的点归为前半部分,接着我们对前半部分再次进行判断三种情

【海量数据处理】N个数中找出最大的前K个数

N个数中找出最大的前K个数,需要用小堆实现. 分析:由于小堆的堆顶存放堆中最小的数据,可以通过与堆顶数据进行比较,将大数据存放在堆中,注意在每次改变堆顶数据后,进行调堆,使堆顶一直存放整个堆中最小元素. void AdjustDown(int *a, size_t root, size_t size)//下调 {//小堆 size_t parent = root; size_t child = parent * 2 + 1; while (child < size) { if (child +