最近在看《剑指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的总数,函数如下所示:

 def get_1_digits(n):
"""
获取每个位数之间1的总数
:param n: 位数
"""
if n <= 0:
return 0
if n == 1:
return 1
current = 9 * get_1_digits(n-1) + 10 ** (n-1)
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就行了。具体代码如下:

 def get_1_nums(n):
if n < 10:
return 1 if n >= 1 else 0
digit = get_digits(n) # 位数
low_nums = get_1_digits(digit-1) # 最高位之前的1的个数
high = int(str(n)[0]) # 最高位
low = n - high * 10 ** (digit-1) # 低位 if high == 1:
high_nums = low + 1 # 最高位上1的个数
all_nums = high_nums
else:
high_nums = 10 ** (digit - 1)
all_nums = high_nums + low_nums * (high - 1) # 最高位大于1的话,统计每个多位数后面包含的1
return low_nums + all_nums + get_1_nums(low)

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

 def get_digits(n):
# 求整数n的位数
ret = 0
while n:
ret += 1
n /= 10
return ret

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

 def test_n(num):
# 常规方法用来比较
ret = 0
for n in range(1, num+1):
for s in str(n):
if s == '':
ret += 1
return ret

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

 if __name__ == '__main__':
test = 9923446
import time
t = time.clock()
print test_n(test)
print time.clock() - t
t1 = time.clock()
print get_1_nums(test)
print time.clock() - t1

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

6970095
18.284745
6970095
0.000223999999999

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

Python解决 从1到n整数中1出现的次数的更多相关文章

  1. 整数中1出现的次数(从1到n整数中1出现的次数)(python)

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  2. 剑指Offer 31. 整数中1出现的次数(从1到n整数中1出现的次数) (其他)

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  3. 剑指Offer(三十一):整数中1出现的次数(从1到n整数中1出现的次数)

    剑指Offer(三十一):整数中1出现的次数(从1到n整数中1出现的次数) 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https:// ...

  4. 30 整数中1出现的次数(从1到n整数中1出现的次数)这题很难要多看*

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  5. 整数中1出现的次数(从1到n整数中1出现的次数)

    题目:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.AC ...

  6. 剑指Offer:面试题32——从1到n整数中1出现的次数(java实现)

    问题描述: 输入一个整数n,求1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11,12,1一共出现了5次. 思路:(不考虑时间效率的解法,肯定不 ...

  7. 题目1373:整数中1出现的次数(从1到n整数中1出现的次数)

    题目1373:整数中1出现的次数(从1到n整数中1出现的次数) 题目描述: 亲们!!我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他 ...

  8. 剑指Offer 整数中1出现的次数(从1到n整数中1出现的次数)

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  9. 1049. Counting Ones/整数中1出现的次数(从1到n整数中1出现的次数)

    The task is simple: given any positive integer N, you are supposed to count the total number of 1's ...

随机推荐

  1. Online Bayesian Probit Regression介绍之Factor Graph

    下面就开始讲讲概率图中的Factor Graph.概率图博大精深,非我等鼠辈能够完全掌握,我只是通过研究一些通用的模型,对概率图了解了一点皮毛.其实我只是从概率这头神兽身上盲人摸象地抓掉几根毛,我打算 ...

  2. php中常用的字符串查找函数strstr()、strpos()实例解释

    string strstr ( string $haystack , mixed $needle [, bool $before_needle = false ] ) 1.$haystack被查找的字 ...

  3. Java历程-初学篇 Day09 冒泡排序

    冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是 ...

  4. java 虚拟机与并发处理几个问题简要(一)

    一.   处理任务时,应该将代码分成不同的部分,每一部分由一个线程进行,但是会因为任务负载不平衡导致有闲有忙.最好是应分成不同的部分,分配不同的线程,尽量让处理器不停的处理,不要闲下来.如何分配线程数 ...

  5. HDU1403Longest Common Substring

    明天写 超时代码: #include<cstdio> #include<cstdlib> #include<iostream> #include<cstrin ...

  6. 项目收集-AutoMapper使用,事务,Json.Net序列化反序列化,代码生成调用等

    using System; using AutoMapper; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Console ...

  7. commons-pool与commons-pool2连接池

    commons-pool和commons-pool2是用来建立对象池的框架,提供了一些将对象池化必须要实现的接口和一些默认动作.对象池化之后可以通过pool的概念去管理其生命周期,例如对象的创建,使用 ...

  8. Python通过future处理并发

    future初识 通过下面脚本来对future进行一个初步了解:例子1:普通通过循环的方式 import os import time import sys import requests POP20 ...

  9. 架构师必备软件:安装Dubbo注册中心(Zookeeper-3.4.6)

    Dubbo建议使用Zookeeper作为服务的注册中心. http://www.roncoo.com/details?cid=f614343765bc4aac8597c6d8b38f06fd 注册中心 ...

  10. 表达式求值--Java实现

    /*将中缀表达式--转化为后缀表达式--属于栈的一种应用 *具体思路: *1.扫描字符串,遇到操作数字符直接不管,存到一个字符串里边 *2.操作符优先级比较--定义了方法 * 栈中优先级高:出栈存进字 ...