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 def get_1_digits(n):
 2     """
 3     获取每个位数之间1的总数
 4     :param n: 位数
 5     """
 6     if n <= 0:
 7         return 0
 8     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 def get_1_nums(n):
 2     if n < 10:
 3         return 1 if n >= 1 else 0
 4     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_nums
12     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 def get_digits(n):
2     # 求整数n的位数
3     ret = 0
4     while n:
5         ret += 1
6         n /= 10
7     return ret

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

1 def test_n(num):
2     # 常规方法用来比较
3     ret = 0
4     for n in range(1, num+1):
5         for s in str(n):
6             if s == ‘1‘:
7                 ret += 1
8     return ret

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

1 if __name__ == ‘__main__‘:
2     test = 9923446
3     import time
4     t = time.clock()
5     print test_n(test)
6     print time.clock() - t
7     t1 = time.clock()
8     print get_1_nums(test)
9     print time.clock() - t1

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

6970095
18.284745
6970095
0.000223999999999

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

时间: 2024-10-15 01:20:49

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

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 in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1's in 1, 10, 11, and 12. Input Specification: Each inp

[Jobdu] 题目1373:整数中1出现的次数(从1到n整数中1出现的次数)

题目描述: 亲们!!我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他.问题是:求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 输入: 输入有多组数据,每组测试数据为一行. 每一行有两个整数a,b(

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

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 思路1:针对这个问题,我们可以有一个最普遍的算法,就是直接计算每一个数中的1出现的次数. public int NumberOf1Between1AndN_Solution(int n) { int count =

剑指offer(四十)之整数中1出现的次数(从1到n整数中1出现的次数)

题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 代码1: <span style="color:#6600cc;">public class Solution { public int NumberOf1Between1AndN_S

n个整数中1出现的次数

整数中1出现的次数(从1到n整数中1出现的次数) (两种方法:1.规律.2暴力求解) 题目描述 求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1 ~ 13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. 规律( 1 的数目) 如果第 i 位(自右向左,从1开始标号)上的数字是0,则第 i 位可能出现 1 的次数

剑指offer之【整数中1出现的次数(从1到n整数中1出现的次数)】

题目: 整数中1出现的次数(从1到n整数中1出现的次数) 链接: https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?tpId=13&tqId=11184&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 题目描述: 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此

剑指offer-第五章优化时间和空间效率(从1到n的整数中1出现的次数)

题目:输入一个整数n,从1到n这n个十进制整数中1出现的次数. 思路1:对1到n中的任意一个数i对其进行求余数来判断个位是否为1,然后再求除数,判断十位是否为1.统计出1的个数.然后对1到n用一个循环将所有的数都加起来.时间复杂度为O(nlogn). 思路2:根据数字的规律来求.例如:21344这个数,我们将它分成两部分,第一部分为1345~21344.这部分我们可以先求出最高位上一出现的次数.又分为两种情况,10000~19999这种情况下最高位大于1,1出现的次数为10^4,另一种是1000

整数中1出现的次数(1~n)

题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了.ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数. #include<iostream> using namespace std; int main() { int n, N, cnt = 0; scanf("%d",&N);/

《剑指offer》整数中1出现的次数(从1到n整数中1出现的次数)

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 题目链接:http://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数