从1到n整数中1出现的次数 算法分析

来源:互联网 发布:php7 mysql 编辑:程序博客网 时间:2024/05/22 00:06

一、问题提出

输入一个正整数n,求从1到n这n个整数的十进制表示中1出现的次数。如若n=12,则从1到12中,包含1的有1,10,11,12,“1”一共出现了5次,故结果应该为5。

二、分析

算法的核心思路是计算出对于从1~n的每一个数,其在每一位上出现的1的个数,然后求和即可。
为了分析,这里我们拿n=12345举例。
我们先看百位,从1~12345这些数中,百位上的“1”一共出现了几次?对于这个问题,我们知道对于这些数,百位上出现“1”的一定具有□□1□□的形式,因此我们要做的就是在方框之中填入数字,使得填入数字后满足组成的数AB1CD小于或等于12345。
显然,若要AB1CD不大于12345,则只需要AB不大于12即可
因此,对于ABCD可以是0000,0001…1201,1202……1298,1299,
对应的数字就是00100,00101……12101,12102……12198,12199。
也就是具有(12+1)*100=1300种可能,因此,对于从1~12345这些数,千位上一共含有1300个1。
同样的分析,对于十位,应该有(123+1)*10=1240个“1”,对于千位,应该有(1+1)*1000=2000个“1”,我们先不考虑个位和万位的边缘情况。
至此,我们似乎发现了规律1:当考察的位上的数字>1时(这里分别是2,3,4)只需要将该位左边的数码所组成的新的数加1,然后乘以考察位的权重即可。

现在我们看第二种情况,n=12004:
对于百位,所有满足条件的数字同样应该具有□□1□□的形式,但是这时,为了使□□1□□或者说AB1CD不大于12004,必须要求AB小于12!
因此,这样ABCD的取值可以是:0000,0001……1101……1199。
对应的原来的数字分别是00100,00101……11101……11199,
一共有12*100=1200个“1”。
同样的,对于十位,自然应当有120*10=1200个“1”
于是,我们发现了规律2:当考察的位上的数字为0时,只需要将该位左边的数码组成的新的数乘以该位的权重即可

接下来看最后一种情形,n=12115
依旧看百位,同样满足条件的数应该具有□□1□□的形式,为了使□□1□□或者说AB1CD不大于12115,我们只需要AB小于12或者AB恰好等于12,对于AB小于12的情况,分析和规律2是完全一致的有12*100=1200种情形,但是这里还有新的可能,就是AB等于12,即121CD必须要不大于12115,当然了,显然有00,01……14,15一共16种可能!
综上,对于百位,一共出现了12*100+15+1=1216个“1”
对于十位,一共出现了121*10+5+1=1216个“1”
于是我们得到了规律3:当考察的位上的数字为1时,只需要将该位左边的数码组成的新的数乘以该位的权重,然后加上该位右边的数码组成的新的数字,再加1即可

我们得到以上这三个重要规律时,前提都是考察的位不处于边缘,但经过简单的检验就可以发现,以上规律对于边缘上的位依旧成立。

三、应用

有了以上的三个规律后,我们就可以轻松的解决这个问题了,为了更好的理解,我们这里就拿n=11023举例,手动算试一试
个位大于1,利用规律1:(1102+1)*1=1103
十位大于1,利用规律1:(110+1)*10=1110
百位是0,利用规律2:11*100=1100
千位是1,利用规律3:1*1000+23+1=1024
万位是1,利用规律3:0*10000+1023+1=1024
于是答案是:1103+1110+1100+1024+1024=5361

四、代码

Counting Ones

0 0
原创粉丝点击