python计算整数区间[m、n所有整数之和_Python解决 从1到n整数中1出现的次数

最近在看《剑指Offer》,面试题32的题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1、10、11和12,1一共出现了5次。

对于书中说的不考虑时间效率的解法很好理解,可以直接完成,但是对于书中介绍的另一种方法,没有理解,于是按照自己的思路进行了分析。

1位数,1-9中,1一共出现了1次;

2位数,10-99中,10-19的十位上一共出现了10*1=10次,对于每个十位开头的数字10-19、20-29,每个数个位上出现的是1-9中1出现的次数,共有9个区间9*1=9次;

3位数,100-999,100-199百位上出现了10**2=100次,对于每个百位数开头,例如100-199,200-299,低位上其实就是0-99这个区间上1出现的次数,一共9个区间 9*19=171次;

由此推测,对于1-9,10-99,100-999,每个n位数中包含1的个数公式为:

f(1) = 1

f(2) = 9 * f(1) + 10 ** 1

f(3) = 9 * f(2) + 10 ** 2

f(n) = 9 * f(n-1) + 10 ** (n-1)

通过以上分析,我们可以确定对于任意一个给定的数,例如23456这个5位数,10000之前的数中包含的个数是确定的了,为f(1)+f(2)+f(3)+f(4),这是一个递归的过程,对此可以求出1-4位中包含1的总数,函数如下所示:

1 defget_1_digits(n):2 """

3 获取每个位数之间1的总数4 :param n: 位数5 """

6 if n <=0:7 return08 if n == 1:9 return 1

10 current = 9 * get_1_digits(n-1) + 10 ** (n-1)11 return get_1_digits(n-1) + current

通过上面的分析,我们知道了23456中,1-10000之间一共出现了多少个1.下一步需要分析10000-23456中包含的1.

我们首先把最高位单独拿出来分析一下,求出最高位上1的个数,如果最高位是1,则最高位上一共会出现的1的次数是低位上数字+1,例如12345,最高位上一共出现了2346个1;如果最高位大于1,则会一共出现的次数是10000-19999一共10**4个数。

然后,根据最高位的不同,计算出该高位前面的相同位数范围中的所有数中1的个数。例如对于34567,需要计算出10000-19999,20000-29999中一的个数,这时候计算一的个数,也就是计算0-9999中1的个数,这就可以转化成上面的f(n)来计算了,调用上面函数可以直接得到,然后用得到的值和最高位和1的差值(这里最高位是3)相乘就可以了。

分析完上面的部分后,我们现在只剩下最高位后面的部分了,我们发现剩下的部分还是一个整数,例如23456剩下了3456,这时候直接使用递归处理剩下的3456就行了。具体代码如下:

1 defget_1_nums(n):2 if n < 10:3 return 1 if n >= 1 else04 digit = get_digits(n) #位数

5 low_nums = get_1_digits(digit-1) #最高位之前的1的个数

6 high = int(str(n)[0]) #最高位

7 low = n - high * 10 ** (digit-1) #低位

8

9 if high == 1:10 high_nums = low + 1 #最高位上1的个数

11 all_nums =high_nums12 else:13 high_nums = 10 ** (digit - 1)14 all_nums = high_nums + low_nums * (high - 1) #最高位大于1的话,统计每个多位数后面包含的1

15 return low_nums + all_nums + get_1_nums(low)

对于上面使用的get_digits函数,是用来求给定的n是几位数的。代码如下:

1 defget_digits(n):2 #求整数n的位数

3 ret =04 whilen:5 ret += 1

6 n /= 10

7 return ret

为了比较运行的效率,我用每次遍历循环每个数中1的个数的方法进行了次数比较,发现使用以上方法效率提高了很多的,给定的数越大,效率提升越明显。常规解法如下:

1 deftest_n(num):2 #常规方法用来比较

3 ret =04 for n in range(1, num+1):5 for s instr(n):6 if s == ‘1‘:7 ret += 1

8 return ret

使用下面的代码进行了测试,发现效率提升非常明显:

1 if __name__ == ‘__main__‘:2 test = 9923446

3 importtime4 t =time.clock()5 printtest_n(test)6 print time.clock() -t7 t1 =time.clock()8 printget_1_nums(test)9 print time.clock() - t1

运行结果如下,发现运行速率提升了很多很多倍:

6970095

18.284745

6970095

0.000223999999999

对于该问题的实现源代码,请在这里获取。

原文:http://www.cnblogs.com/qiaojushuang/p/7780445.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值