您的位置:首页 > 其它

输入任意一个数,得到1到这个数之间的1的个数

2011-06-14 19:47 453 查看
好久没写博客了,忙着面试,现正学习各种算法,今天碰到一个题。就是题目所写的那样,输入任意一个数,得到1到这个数之间的1的个数。比如这个数是12,那么函数f(12)返回的是5(因为中间有5个1)。

另外说明一下,由于个人水平有限,算法的效率有待提高,欢迎高手指点。

function one_ques($num){
$count=0;
$str="";
if($num!=""||$num>=0){
for($i=1;$i<=$num;$i++){
$str.=$i;
}
echo $str;
for($j=0;$j<=strlen($str);$j++){
$str1=substr($str,$j,1);
if($str1==1){
$count++;
}
}
return "从1到".$num."有".$count."个1";
}else{
return false;
}
}
$num=1234;
var_dump(one_ques($num));


当$num=100000000时,代码大概执行了50秒,并且是在修改了30秒运行超时的错误和内存溢出的错误之后,说明代码的问题很大,应该优化php代码。

仔细分析这个问题,给定了N,似乎就可以通过分析"小于N的数在每一位上可能出现 1 的次数"之和来得到这个结果让我们 来分析一下对于一个特定的 N,如何得到一个规律来分析在每一位上所有出现 1 的可能性,并求和得到最后的 f(N)。

先从一些简单的情况开始观察,看看能不能总结出什么规律。

先看 1 位数的情况。

如果 N = 3,那么从 1 到 3 的所有数字:1、2、3,只有个位数字上可能出现 1,而且只出现 1 次,进一步可以发现如果 N 是个位数,如果 N>=1,那么f(N)都等于 1,如果 N=0,则 f(N)为 0。

再看 2 位数的情况。

如果 N=13,那么从 1 到 13 的所有数字:1、2、3、4、5、6、 7、8、9、10、11、12、13,个位和十位的数字上都可能有 1,我 们可以将它们分开来考虑,个位出现 1 的次数有两次:1 和 11,十位出现 1 的次数有 4 次:10、11、12 和 13,所以 f(N)=2+4=6。要注意的是 11 这个数字在十位和个位都出现了 1,但是 11 恰好在个位为 1 和十位为 1 中被计算了两次,所以不用特殊处理,是对的。再考虑 N=23 的情况,它和 N=13 有点不同,十位出现 1 的次数为 10 次,从 10 到 19,个位出现 1 的次数为 1、11 和 21,所以f(N)=3+10=13。通过对两位数进行分析,我们发现,个位数出现 1 的次数不仅和个位数字有关,还和十位数有关:如果 N 的个位数大于等于 1,则个位出现 1 的次数为十位数的数字加 1;如果N 的个位数为 0,则个位出现 1 的次数等于十位数的数字。而十位数上出现 1 的次数不仅和十位数有关,还和个位数有关:如果十位数字等于 1,则十位数上出现 1 的次数为个位数的数字加 1;如果十位数大于 1,则十位数上出现 1 的次数为 10。

f(13) = 个位出现1的个数 + 十位出现1的个数 = 2 + 4 = 6;

f(23) = 个位出现1的个数 + 十位出现1的个数 = 3 + 10 = 13;

f(33) = 个位出现1的个数 + 十位出现1的个数 = 4 + 10 = 14;

...

f(93) = 个位出现1的个数 + 十位出现1的个数 = 10 + 10 = 20;

接着分析 3 位数.

如果 N = 123:

个位出现 1 的个数为 13:1, 11, 21, ¼, 91, 101, 111, 121

十位出现 1 的个数为 20:10~19, 110~119

百位出现 1 的个数为 24:100~123

f(23)= 个位出现 1 的个数 + 十位出现 1 的个数 + 百位出现 1 的次数 = 13 + 20 + 24 = 57;

同理我们可以再分析 4 位数、5 位数。读者朋友们可以写一写, 总结一下各种情况有什么不同。

根据上面的一些尝试,下面我们推导出一般情况下,从 N 得到 f(N)的计算方法:

假设 N=abcde,这里 a、b、c、d、e 分别是十进制数 N 的各个数位上的数字.如果要计算百位上出现 1 的次数,它将会受到三个因素的影响:百位上的数字,百位以下(低位)的数字,百位(更高位)以上的数字。

如果百位上的数字为 0,则可以知道,百位上可能出现 1 的次数由更高位决定,比如 12 013,则可以知道百位出现 1 的情况可能是100~199,1100~1 199,2100~2199,¼,11100~11199,一共有 1200 个。也就是由更高位数字(12)决定,并且等于更高位数字(12)×当前位数(100).

如果百位上的数字为 1,则可以知道,百位上可能出现 1 的次数不仅受更高位影响,还受低位影响,也就是由更高位和低位共同决定。例如对于 12113,受更高位影响,百位出现 1 的情况是 100~199,1100~1199,2100~2199,¼,11100~11199,一共 1200个,和上面第一种情况一样,等于更高位数字(12)×当前位数(100)但是它还受低位影响,百位出现 1 的情况是 12 100~12 113,一共114 个,等于低位数字(123)+1。如果百位上数字大于 1(即为 2~9),则百位上可能出现1的次数也仅由更高位决定,比如 12 213,则百位出现 1 的可能性为:100~199,1 100~1 199,2 100~2 199,¼,11 100~11 199,12 100~12 199,一共有 1 300 个,并且等于更高位数字+1(12+1) ×当前位数(100).

通过上面的归纳和总结,我们可以写出如下的更高效算法来计算 f(N):

function Sum1s($n)
{
$iCount = 0;
$iFactor = 1;
$iLowerNum = 0;
$iCurrNum = 0;
$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 intval($iCount);
}

$n=134;
var_dump(Sum1s($n));


这个函数还有点瑕疵,返回的结果不对貌似,希望高手指点一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐