两个精巧的算法

1、计算一个二进制数中1的个数

如给定一个整型数 x = 10,它的二进制表达式是(1010)B,题意就是计算出x的二进制表示中一的个数是2。
分析如下:
如果将x转化为二进制,就可以计算出1的个数了,此时复杂度是o(log n)的,复杂度还可以。
int func(int x){
	int count = 0;
	while(x){
		if(x%2 == 1) count++;
		x /= 2;
	}
	return count;
}

但是如果要继续降低复杂度,那就是常数级别的了,想想都很牛逼,确实,这个算法真实存在。
分析如下:
以x = 10为例,二进制表示(1010)B
x                  x-1               x&(x-1)
1010        1001             1000
因为减1就是最低位的那个1借位的,所以当x&(x-1)自然就去掉了一个想要计算的1,如此循环,直到x==0,也就算完了所有的1。真是妙!
x                  x-1               x&(x-1)            count
1010        1001             1000                   1
1000         0111             0000                  2

int func2(int x){
	int count = 0;
	while(x){
		x = x & (x - 1);
		count ++;
	}
	return count;
}
此时的复杂度就是o(m),m为x中1的个数。
显然这个算法的复杂度更低。当二进制数中1的个数比较稀疏的时候,效率尤其高。

2、已知有随机生成1和7之间数的函数,设计生成1到10之间随机数的函数

乍一看,觉得很简单,也就是rand7()/7*10,但是这样做了以后会发现,有些数字是不可能出现的,也就是这个算法导致各个数出现的概率不均匀了。
分析:
运算的目的在于产生均匀分布的1-10,可是我们只有1-7,所以就要相办法产生数8-10,并且要求均匀。

这里提出一种叫做拒绝采样的方法。主要的思想是如果产生的随机数是目标范围内的,就直接返回,如果不是目标范围内的数,就丢弃,重新采样。
只要保证目标范围内的数是均匀分布的,就达到目的了。

均匀分布构造如下:[ rand7() +  ( rand7() - 1 ) * 7 ] % 10 + 1
1  2  3  4  5  6  7
1  1  2  3  4  5  6  7
2  8  9 10  1  2  3  4
3  5  6  7  8  9 10  1
4  2  3  4  5  6  7  8
5  9 10  1  2  3  4  5
6  6  7  8  9 10  1  2
7  3  4  5  6  7  8  9
1  2  3  4  5  6  7
1  1  2  3  4  5  6  7
2  8  9 10  1  2  3  4
3  5  6  7  8  9 10  1
4  2  3  4  5  6  7  8
5  9 10  1  2  3  4  5
6  6  7  8  9 10  *  *
7  *  *  *  *  *  *  *
如上,首先产生的是49个数,这49个数显然不能保证概率的均匀性,但是前40个数据能保证是均匀分布的,因此可以舍弃后9个数据。
这样舍弃后的数据就是均匀分布的40个数据了。
int rand10() {
	int idx;
	do {
		idx = rand7() + ( rand7() - 1 ) * 7;
	} while (idx > 40);
	return 1 + (idx - 1) % 10;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值