1 的数目(编程之美2.4)——我的方法

本文深入分析并推导出一种计算指定范围内数字中'1'出现次数的算法,涵盖从两位数到四位数的具体步骤,并提供递归实现方法。详细解释了如何根据数字的位数和最高位的特性来简化计算过程。

题目如下:


 一、初步思路

 

先按照题目,简单写下几组数字观察分析一下。


 通过观察,我们发现:对于两位数而言,影响“1”的数目的因素主要表现在红色和蓝色标记的地方。

 

下面我们再写一些三位数的数字观察分析一下。


通过观察,我们发现:第五组灰色阴影标识的部分除了最高位,其他的和前面是重复的;对于101~199,最高位的“1”是不需要计算的,而除去最高位,剩下的部分则在二位数的情形中已经计算过了。对于201~999,最高位数字对于“1”的计算没有影响,因为它们不含“1”,而除去最高位的部分,则和二位数的情形一样。

 

二、特例研究

 

通过上面的分析,我们发现,“1”的数目和数字的位数是有关系的,和最高位的数字也是有关系的,另外,除去最高位部分,剩下的数字中“1”的个数的情形在前面已经出现过了。

 

另一方面,我们也发现,1~9,1~99, 1~999,1~9999……这些特殊范围中,数字“1”出现的次数是有规律的,总结一下就是:如果k = 999……999一共有m位,那么1~k中数字“1”出现的次数为bm= m * 10m-1

 

现在我们继续使用上面几组数字(假设数字的位数是3):

1. 如果最高位是“1”,不妨设我们写下的数字是132,那么我们可以分为三部分来考虑:

(1)先算出1~99中数字“1”出现的次数;

(2)在100~132中,数字“1”在最高位共出现了33次;

(3)101~132除去最高位部分的数字中,数字“1”的个数和1~32中数字“1”出现的次数是一样的,我们可以利用递归来解决它。

2.如果最高位大于“1”,不妨设我们写下的数字是372,那么我们还可以分为三部分来考虑:

(1)在1~300中,“1~99”(以及“01~99”)共出现了3次;

(2)百位数字出现数字“1”的情形只在100~199中;

(3)在301~372除去最高位部分的数字中,数字“1”的个数和1~72中数字“1”出现的次数是一样的,我们可以利用递归来解决它。


三、公式提取

 

按照上述方式,我们再分析一下四位数的情形,就可以发现基本规律了。

 

设从1开始写,写到K,K的形式为:amam-1……a2a1

则有下述公式:

 

公式中,有关bm的定义请查看上一节。

 

四、算法实现

 

#include<math.h>

int count_number(int N)
{
    if(0 == N)    return 0;
    if(N <= 9 && N >= 1)    return 1;
    
    int m = 0, h;    //m用来存储位数,h用来存储最高位数字
    int tmp = N;
    while(tmp)
    {
        m++;
        h = tmp % 10;
        tmp = (int)(tmp / 10);
    }

    int tmp_1 = (m - 1) * pow(10, m - 2);
    int tmp_2 = N - h * pow(10, m - 1);

    if(1 == h)    return (tmp_1 + tmp_2 + 1 + count_number(tmp_2));

    return (h * tmp_1 + pow(10, m - 1) + count_number(tmp_2));
}



<—— 原创作品,转载请注明:http://blog.youkuaiyun.com/Phoenix_W_2010 ——>


——————————————————————————————————————————————————————

后记:

这是我在博客写的第一篇文章。我尽量按照自己当时的思考过程来写的,也不知道这篇文章表述清楚了没有,如果大家读得很费劲,请多多包涵,O(∩_∩)O~。如果有什么错误,也请大家在回复中指出,我会修改的。

算法用到了递归,后来读了一下《编程之美》上面给出的方法,觉得自己水平还是差了很多啊,需要继续努力,O(∩_∩)O~。

好了,就写到这里吧。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值