求1到n的所有正整数中1出现的次数

分析

设n的位数为L。1到n的所有整数中1的个数由以下(1)(2)两部分构成

(1)长度小于L的所有正整数中的1

           包含i个1的数有 C(i, L-1)*9^(L-1-i)个,其中包含0个1的情况中有这个数字是0的情况,不过不影响对1的计数。故长度小于L的所有正整数中1的个数为C(i, L-1)*9^(L-1-i)*i

(2)长度为L且小于n的所有正整数中的1

          从最高位开始,将n的每一位计算出来设为 a1, a2, ..., aL, 前n位包含1的个数为count[n]

          从前往后处理每一位

          sum=0;
          Loop:
          A:m位为0,该位置不能改变,继续处理下一位,下一位的计数包含了这种情况
          B:m位大于0
            B1:m位为1,则令m位为0,后面的位任意取数,1的个数为 sum{i=0~L-m}{C(i, L-m)*9^(L-m-i)*(i+count[m])}
            B2:m位大于1
               B2A:m位取1,一的个数为sum{i=0~L-m}{C(i, L-m)*9^(L-m-i)*(1+i+count[m])}
               B2B:m位不取1,1的个数为 (am-1)*B1
          将对应情况的计数累加到sum上
          循环。
将(1)(2)两部分的计数相加。

改进

考虑到各位上的循环现象,有更简单的方法

# -*- coding: cp936 -*-
import time
def getNumberOfDigits(n):
    res = 0
    base = 10;

    # 从最低位到最高位,分别计算所有数字在该位上出现1的次数,累加到res上
    while(10*n/base != 0):
        # 当前位每一个完整循环中,1出现的次数
        a = n/base
        # 当前位完整循环的次数
        b = base/10
        # 加上当前位完整循环中1出现的次数
        res += b*a
        # 计算当前为剩下的没有循环完整的数列中的1的个数
        if(n%base >= 2*b):
            # 虽然没有循环完,但当前循环中所有1已经出现完了
            res += b
        else:
            # 当前循环只出现了部分1
            res += n%base-b+1
        base *= 10
    return res

def test(n):
    time1= time.clock()
    res = getNumberOfDigits(n)
    time2= time.clock()
    print n, "\t", res, "\t", (time2-time1)*1000, "ms"  

# 测试
for i in range(1, 30):
    test(i)
    
test(987654)
test(9876543)
test(98765432)
test(987654321)

输出为

1 	1 	0.00434199442804 ms
2 	1 	0.00355254089567 ms
3 	1 	0.00315781412948 ms
4 	1 	0.0197363383092 ms
5 	1 	0.00355254089567 ms
6 	1 	0.00986816915463 ms
7 	1 	0.00394726766184 ms
8 	1 	0.00394726766184 ms
9 	1 	0.00355254089568 ms
10 	2 	0.0201310650754 ms
11 	4 	0.00513144796038 ms
12 	5 	0.00473672119422 ms
13 	6 	0.0276308736329 ms
14 	7 	0.0248677862696 ms
15 	8 	0.0055261747266 ms
16 	9 	0.0236836059711 ms
17 	10 	0.004341994428 ms
18 	11 	0.0189468847769 ms
19 	12 	0.025657239802 ms
20 	12 	0.0224994256726 ms
21 	13 	0.0118418029855 ms
22 	13 	0.0244730595035 ms
23 	13 	0.004341994428 ms
24 	13 	0.00473672119417 ms
25 	13 	0.00434199442811 ms
26 	13 	0.004341994428 ms
27 	13 	0.0256572398021 ms
28 	13 	0.00394726766184 ms
29 	13 	0.00473672119428 ms
987654 	595336 	0.0232888792049 ms
9876543 	6941015 	0.00868398885601 ms
98765432 	79286694 	0.0205257918416 ms
987654321 	891632373 	0.0374990427876 ms


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值