实现一个函数,对一个正整数n,算得到1需要的最少操作次数:

转载部分:http://zhiqiang.org/blog/science/computer-science/complexity-of-recursive-algorithm.html

递归算法的复杂度通常很难衡量,一般都认为是每次递归分支数的递归深度次方。但通常情况下没有这个大,如果我们可以保存每次子递归的结果的话,递归算法的复杂性等于不同的节点个数。这也是动态规划算法思想的由来。

看一下下面这个算法题目,据称是百度的笔试题:

简述:实现一个函数,对一个正整数n,算得到1需要的最少操作次数:

如果n为偶数,将其处以2;如果n为奇数,可以加1或减1;一直处理下去。

要求:实现函数(实现尽可能高效)int func(unsigned int n);n为输入,返回最小的运算次数。

我不确定是不是对n的操作次数有一个简单的刻画,尝试着想了一会儿,似乎不太容易想到。但后来发现这个题目本质上不是算法题,而是算法分析题。因为仔细分析可以发现,题目中给的递归构造本身就是非常高效的。

直接按照题目中的操作描述可以写出函数:

int function(unsigned int n) {
  if (n == 1) return 0;
  if (n%2 == 0) return 1 + function(n/2);
  return 2 + min(function((n + 1)/2), function((n - 1)/2));

在递归过程中,每个节点可以引出一条或两条分支,递归深度为 logn ,所以总节点数为 n 级别的,但为何还说此递归本身是非常高效的呢?

理解了动态规划的思想,就很容易理解这里面的问题。因为动态规划本质上就是保存运算结果的递归,虽然递归算法经常会有指数级别的搜索节点,但这些节点往往重复率特别高,当保存每次运算的节点结果后,在重复节点的计算时,就可以直接使用已经保存过的结果,这样就大大提高了速度(每次不仅减少一个节点,而且同时消灭了这个节点后面的所有分支节点)。

在这个问题里是什么情况呢?仔细分析就会发现,在整个搜索数中,第 k 层的节点只有两种可能性 n>>k n>>k1 。这意味着整个搜索树事实上只有 2logn 个节点。所以这个递归算法本质上的运算复杂度只有 O(logn) 。这已经是最优的了。


原创部分:

 以上为O(logn)复杂度。

方法二:

思路:对于偶数,除以2,(向右移动一位),对于奇数,减去1再右移一位,即:求取二进制中1的个数和n用二进制表示的位数之和。

     时间复杂度为二进制中1的个数。

转自:http://www.matrix67.com/blog/archives/3985#more-3985

下面这个位运算小技巧可以迅速给出一个数的二进制表达中末尾有多少个 0 。比如, 123 456 的二进制表达是 1 11100010 01000000 ,因此这个程序给出的结果就是 6 。

unsigned int v;  // find the number of trailing zeros in 32-bit v
int r;           // result goes here
static const int MultiplyDeBruijnBitPosition[32] =
{
  0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
  31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];


#include <iostream>
using namespace std;
int deep=0;

static const int MultiplyDeBruijnBitPosition[32] =
{
	0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
	31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};

int cal(int n);
int cal1(int n);

int main()
{
	int n=15;
	cout<<cal(n)<<endl;
	cout<<cal1(n)<<endl;
	cout<<deep<<endl;
	return 0;
}

int cal(int n)
{
	int sum=-1;
	int number=0;
	while (n)
	{
		number++;
		int r= MultiplyDeBruijnBitPosition[((unsigned int)((n & -n) * 0x077CB531U)) >> 27];
		n>>=r;
		sum+=r;

		n &=n-1;
		sum+=1;
	}
	cout<<number<<endl;
	return sum;
}

int cal1(int n)
{
	deep++;
	if (n==1)
		return 0;
	int m=1;
	if (n%2)
		m++;
	return cal1(n>>1)+m;
}


还没有仔细看递归的时间复杂度计算方法,以后再仔细研究一下。







  





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值