题目:求1~n的整数中1的个数。
常规做法是,先写一个右移判1的子函数,计算每个数里面的1的个数,然后遍历1~n,取和,就是答案。
在百度文库(http://wenku.baidu.com/view/6722a969af1ffc4ffe47ac18.html)中看到有这样一个解法:
//计算整数 1~n 中的1的个数总和
ULONGLONG Sum1s(ULONGLONG n)
{
ULONGLONG iCount = 0;
ULONGLONG iFactor = 1;
ULONGLONG iLowerNum = 0;
ULONGLONG iCurrNum = 0;
ULONGLONG iHigherNum = 0;
while(n / iFactor != 0)
{
iLowerNum = n - (n / iFactor) * iFactor;
iCurrNum = (n / iFactor) % 10;
iHigherNum = n / (iFactor * 10);
switch(iCurrNum)
{
case 0:
iCount += iHigherNum * iFactor;
break;
case 1:
iCount += iHigherNum * iFactor + iLowerNum + 1;
break;
default:
iCount += (iHigherNum + 1) * iFactor;
break;
}
iFactor *= 10;
}
return iCount;
}
据它说“ 这个方法只要分析N就可以得到f(N),避开了从1到N的遍历,输入长度为Len的数字N的时间复杂度为O(Len),即为O(ln(n)/ln(10)+1)。在笔者的计算机上,计算N=100 000 000,相对于第一种方法的40秒时间,这种算法不到1毫秒就可以返回结果,速度至少提高了40 000倍。”
但不知为何没能运行正确,尽管没有看懂思路,但觉得还蛮有启发,直接从 n 入手分析,避免遍历1~n,于是我研究一会之后,观察到有这么一个现象:在二进制表示中,N位的000...000 ~111...111 之间0 与 1的个数相等!如下图所示:
于是基于递归法的实现方法如下所示,并且递归深度不会超过 n 的最高非0 bit位数,比较快吧?
//计算整数 1~n 中的1的个数总和
ULONGLONG Sum1s(ULONGLONG n)
{
if (n <= 2)
{
return n;
}
ULONGLONG iFactor = 3;
ULONGLONG iLen = 1;
while(n >= iFactor) //找出最大的、小于n的、bit位全部为1 的数
{
iFactor <<=1;
iFactor |= 1;
++iLen;
}
iFactor >>=1;
ULONGLONG iCount =((iFactor + 1)*iLen)>>1; //这一片区域的1 0 数量相等
if ( iFactor == n)
{
return iCount;
}
else
{
return (iCount + n - iFactor + Sum1s(n & (iFactor)));
}
}