输入任意一个数,得到1到这个数之间的1的个数
2011-06-14 19:47
453 查看
好久没写博客了,忙着面试,现正学习各种算法,今天碰到一个题。就是题目所写的那样,输入任意一个数,得到1到这个数之间的1的个数。比如这个数是12,那么函数f(12)返回的是5(因为中间有5个1)。
另外说明一下,由于个人水平有限,算法的效率有待提高,欢迎高手指点。
当$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 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));
这个函数还有点瑕疵,返回的结果不对貌似,希望高手指点一下。
相关文章推荐
- 编写一个Java应用程序。用户从键盘输入一个1—9999之间的数,程序将判断这个数是几位数, 并判断这个数是否是回文数。回文数是指将该数含有的数字逆序排列后得到的数和原数相同, 例如12121、3
- 2. 编写一个Java应用程序,用户从键盘输入一个1~9999之间的数,程序将判断这个数是几位数,并判断这个数是否是回文数。回文数是指将该数含有的数字逆序排列后得到的数和原数相同,例如12121、32
- 编写一个Java应用程序。用户从键盘输入一个1—9999之间的数,程序将判断这个数是几位数,并判断这个数是否是回文数。回文数是指将该数含有的数字逆序排列后得到的数和原数相同,例如12121、3223都
- CareerCup2.3 删除最中间那个,或者删除给出任意一个节点(只能得到这个节点的pointer)
- 得到两个long型数字之间的任意一个数
- 任意一个5位数,比如:34256,把它的各位数字打乱,重新排列,可以得到一个最大的数:65432, 一个最小的数23456。求这两个数字的差,得:41976,把这个数字再次重复上述过程(如果不足5位,
- 输入任意一个整数,编程判断这个数的奇偶性。
- 输入一个递增的数组和一个数字s,找出数组中任意两个数使他们的和正好是这个数字s,如果有任意多对数字的和为s,输出任意一堆数字即可,例如,输入数组[1,2,4,7,11,15]和数字
- google笔试题:写出这样一个函数 ,输入一个 n, 输出从1到这个数字之间的出现的1的个数
- 谷歌面试题:输入是两个整数数组,他们任意两个数的和又可以组成一个数组,求这个和中前k个数怎么做?
- 输入一个表得到这个表的查询结果
- 谷歌面试题:输入是两个整数数组,他们任意两个数的和又可以组成一个数组,求这个和中前k个数怎么做?
- 谷歌面试题:输入是两个整数数组,他们任意两个数的和又可以组成一个数组,求这个和中前k个数怎么做?
- 一个很好的国家autocomplete插件,输入国家code,就能得到国家名字,甚至能得到国家的currency(这个是我一个个手动录进去)
- 输入一个日期,计算与所设定日期之间的天数,输出这个日期是星期几
- 给你一个单词a,如果通过交换单词中字母的顺序可以得到另外的单词b,那么定义b是a的兄弟单词。现在给你一个字典,用户输入一个单词,让你根据字典找出这个单词有多少个兄弟单词。
- JAVA--第2周实验--用户从键盘输入一个1~9999之间的数,程序将判断这个数是几位数,并判断这个数是否是回文数
- 输入一个数 可得到这个数的十次加如输入1 可算 1+11+111+1111.........(十次)
- 想一个1到100之间的数,分别除以357得到3个余数,把这三个余数输入计算机,计算机会猜出你想的数
- 堆的数据结构能够使得堆顶总是维持最大(对于大根堆)或最小(对于小根堆),给定一个数组,对这个数组进行建堆,则平均复杂度是多少?如果只是用堆的 push 操作,则一个大根堆依次输入 3,7,2,4,1,5,8 后,得到的堆的结构示意图是下述图表中的哪个?