问题:

写一个函数,计算4 000 000 000 以内的最大的那个f(n)=n的值,函数f的功能是统计全部0到n之间全部含有数字1的数字和。比方:f(13)= 6,由于“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的总数是6(1,10,11,12,13)。

分析:

一、简单算法 — 枚举

採用“枚举法”对每一个数都计算一遍1的个数。直到枚举完给定范围全部数,找到符合f(n)=n的数。此方法,代码效率极低。运算所需时间巨大。

python版本号代码例如以下:

  1. # -*- coding:utf-8 -*-
  2. # 问题:写一个函数。计算4 000 000 000 以内的最大的那个f(n)=n的值,
  3. # 函数f的功能是统计全部0到n之间全部含有数字1的数字和。比方:f(13)= 6,
  4. # 由于“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的总数是6(1,10,11,12,13)。
  5. # by chasdmeng
  6. import time
  7. def Calculation_One_Times(n):
  8. res = 0
  9. while n > 0:
  10. if n == 1:res +=1
  11. n /=10
  12. return res
  13.  
  14. if __name__ == '__main__':
  15. times = 0
  16. i = 0
  17. gMAX = 4000000000L
  18. start = time.time()
  19. while i < gMAX:
  20. times +=Calculation_One_Times(i)
  21. if times ==i:print "f(", i, ") = ", times
  22. i +=1
  23. end = time.time()
  24. print "time:",end - start

  二、高效算法 — 剪枝

主要採用剪枝算法对代码运行效率进行优化。求解MAX(f(n)=n)详细算法构建分析例如以下:

1、求数字“0~n”中“1”的个数

基本思想:

t位数字n,求0到n全部数字“1”的数字和。可对1到t位(1相应个位)。每一个位置上“1”出现的次数分别统计,然后求和。

如果。0到n之间的自然数范围内。统计第m位为“1”的个数和。能够分四步骤。第一步骤,如果第二、三步骤的前提条件为第1到第m-1位内各位数字取固定值a。第二步骤。求大于m位的数字中有多少个数字第m位为“1”。

第三步骤,求等于m位的数字中有多少个数字第m位为“1”。

第四步骤,把第1到第m-1位内各位数字变化考虑进去。例如以下图所看到的。

在第二步骤中,须要分两种情况考虑:

(a)给定数n的第m位非0

大于m位的数字中第m位为“1”的数字个数是n/10^m。

(b)给定数n的第m位为0

       对于m位的数字中第m位为“1”的数字个数为(n/10^m)-1。这里举例说明,n=300。m=2。由于300的第2位为0。此时n/10^m=3。若按(a)中方法求解得出在第1位固定取a 的条件下(第一步骤中规定的)大于2位的数字中第2位为“1”的数字个数是3,这显然是错误的,由于在0到300中这种数仅仅有“11a”、“21a”,此时正确个数应该是n/10^m-1=2。

      (c)合并考虑

大于m位的数字中第m位为“1”的数字个数为(n/(10^(m-1))-1)/10。构造了一个通用公式能够包括(a)、(b)两种情况。

原理是。n/10^m=(n/(10^(m-1)))/10,因为是对n做除是向下取整运算,在给定数n第m位非0情况下。n/10^m=(n/(10^(m-1)))/10=(n/(10^(m-1))-1)/10;而当给定数n第m位为0情况下,(n/10^m)-1=(n/(10^(m-1))-1)/10。能够举详细数字对其验证。

       在第三步骤中,因为在第一步骤假定的前提条件。满足求等于m位的数字中第m位为“1”的个数仅仅有1个。

这里举例说明,n=300。m=2。2位数字中第2位为“1”的数仅有“1a”。

       在第四步骤中,综合第一、二、三步骤后。在第1到第m-1位内各位数字取固定值a的条件下,第m位为“1”的个数和为(n/(10^(m-1))- 1)/10+1。

以下把第1到第m-1位内各位数字变化考虑进去,因为在第1到第m-1位中每一位能够放0~9之间随意数。m-1位数共同拥有“10^m-1”个。

终于,0到n之间的自然数范围内,第m位为“1”的个数和((n/(10^(m-1))- 1)/10+1)*(10^(m-1))。

       小结:对以上分析结果给予公式化定义。Cm=((n/i - 1)/10 + 1)*i,当中i=10^(m-1),n为随意自然数。Cm为0到n的数中第m位为1 的 个数和。

求数字“0~n”中“1”个数的GetTimes()函数,python版代码例如以下:

  1. def GetTimes(n):
  2. temp = n
  3. temp2 =1
  4. ret = 0
  5. i = 1
  6. while temp/i:
  7. ret +=(((temp/i-1)/10)+1)*i
  8. if(((temp/i)%10) == 1):
  9. ret -=i
  10. ret +=temp2
  11. i*=10
  12. temp2=n%i+1
  13. return ret

2、从0到n位最大数之间1的总个数,寻找数学规律

当n=1,2,3,...,6,...时。分别计算从0到n位最大数之间1的总个数,部分数据例如以下:

0~9          :1

       0~99        :20 = 10*1 + 10

       0~999      :300 = 10*20 + 100

       0~9999    :4000 = 10*300 + 1000

       0~99999  :50000 = 10*4000 + 10000

       0~999999:600000 = 10*50000 + 100000

       …                   …

从中。能够发现例如以下规律:

a1 = 1

       a2 = 10*a1 + 10

       …

       an = 10*an - 1 + 10^(n - 1)

       即0~9…9(n个9)之间1的个数为an=10*an-1+10^(n-1)个。

这样,当计算随意一个数字n的0到n之间全部数字1的个数和时,能够採用将数字n拆分成0~9、0~99... 等部分进行之间计算。

比如:n=123。可将n拆分为0~99和100~123计算:(1)0~99相应1的个数和直接可知为20;(2)百位数1的个数为23+1个。(3)剩下的0~23继续拆分为0~9、10~19、20~23,共两个0~9个数为2;(4)由于23中十位数2>1,十位为1的个数10;(5)如今就剩下20~23了,个数为1。所以,0~123中1的个数为20+24+2+10+1=57。

因此,在求随意数0~n内全部1的个数和时,能够首先建立“int  gTable[10]”结构用于存放a1、a2、a3、...、a9、a10。当中an表示0~n内全部1的个数,然后将数n拆分为0~9、0~99... 等部分直接使用gTable的值进行计算。

这样的方式能够提高算法效率。

生成gTable[]结构的TimesTable()函数。python版代码例如以下:

  1. def TimesTable(n):
  2. return [GetTimes(10**(i+1) - 1) for i in xrange(n)]

3、採用剪枝算法搜索0~n之间满足f(q)=q的数

因为若对0~n的全部数据进行枚举验证f(q)=q,当n值非常大时运算相当费时,从本文开头给出的“最简单方法”中能够感受到代码效率有多么低。为了优化算法提高效率可採用减枝算法,其思想是对0~n之间全部数建立搜索树,对不符合条件的搜索树进行剪枝,这样就能够极大缩小搜索范围,提高算法效率。

怎样构建剪枝规则呢?首先对这个问题进行数学化描写叙述:

定义1:设 f(x)=y,当中x∈ (0,n),f为x与相应的(0,x)上全部数字1的个数总和y的映射。

先观察下f(n)的特点,能够发现f(n)是一个单调递增函数,这样能够归纳出它的特性:

性质1:若有f(m)=a。f(t)=b,且m<t,则存在q∈(m。t)满足f(q)=q成立的条件是:b>m 且a< t。

如今就能够应用“性质1”进行减枝操作。详细来说通过“性质1”可知,当对0~n的全部数据进行枚举验证f(q)=q时,若当f(t)=b<m或f(m)=a>n,则在[m。t]范围内不存在满足f(q)=q成立的数q,这样就可忽略掉[m,t]内这些自然数,直接跳到n+1往后继续枚举。在应用剪枝算法枚举符合条件的f(q)=q时。是以t-m为步长在0~n内逐段排查是否可剪枝,若t-m范围内不满足剪枝条件则进行枚举搜索。因此在进行剪枝算法时[m,t]的范围取多大适合呢?这是须要谨慎考虑的问题,合理的[m,t]取值能让大幅提升算法运行效率。

以下就对[m,t]步长L取值进行分析。

[m,t]的取值根据是能最大化提升算法效率,因为在“2、从0到n位最大数之间1的总个数。寻找数学规律。”中设置了“int  gTable[10]”结构用于计算f(n),gTable分别表示0~9中1个数和1、0~99中1个数和20、0~999中1的个数和300...。可见是以10^p倍增长,并且f(n)还须要使用gTable去求解,因此初步考虑将[m,t]步长L=10^p。尽管[m,t]取10的倍数,但p详细取多少,还须要继续分析。

对于随意s位数m。设s位数的最大值为max(s),则f(max(s))=gTable[s-1],假定取[m,t]步长L=10^s也就是 t=m+10^s,设f(m)=a、f(m+10^s)=b。则有m<max(s)<m+10^s,由于f()为单调增函数,推出gT[s-1]=f(max(s))<f(m+10^s)=b。a<gTable[s-1]。又由于在0~10^10-1的范围内有max(s)>gTable[s-1](能够举例验证),推出a<gTable[s-1]<max(s)<10+10^s。即a<m+10^s。f(m+10^s)=f(10^s-1)+m+1+f(m)=b。即b>m,因此依据性质1可知m=d*10^s时,取[m。t]步长L=10^s恒不满足减枝条件。同理步长L=10^(s+1)也无法满足剪枝条件,因此仅仅能往下取值。对于随意s位数m,取步长L<=10^(s-1)某些数内能够满足减枝条件。举例来说,m=20,s=2,L=10^(s-1)=10。则在[20。30]内f(20)=12、f(30)=13。依据性质1能够剪枝掉[20。30]范围内的数。综上所述,对随意s位数m,m∈
(0,n),剪枝算法初始步长L=10^(s-1)   。

性质2:若有f(m)=a,f(t)=b,且m<t,满足b>m 且a< t时,有f(q)=q,且q∈(m。b]。

性质2是由性质1推到而来。性质2中将原来性质1中q∈(m,t)缩小为q∈(m。b)。首先在0~10^10-1的范围内满足性质1条件下可知b∈(m。t],设f(b+1)=c。则在(b+1,t)内f(b+1)=c、f(t)=b依据性质1不存在q∈(b+1,t)使f(q)=q成立。

所以,在(m,t)内使f(q)=q成立的q∈(m,b]。

剪枝算法总体思想是:n位数m,步长L=10^(n-1)。在区间(m-1。m+L-1)推断是否满足f(m-1)>boundary或f(m+L-1)<m
。满足就直接跳到m=m+L计算,否则在区间(m-1,f(m+L-1)+L-1)上置步长L=L/10递归反复运行剪枝推断,当递归到步长L=1时,进行逐值枚举f(number)是否等于number。因为算法中m是从0開始枚举取值,以10的倍数递增。因此全部的m都应满足abc*10^s格式,也就是说,m取到的是10的倍数。因此有例如以下性质。

性质3:m=abc*10^(n-1)。L=10^(n-1)。可知f(m-1+L)=f(m-1)+gTable[n-2]+count_one_m*L。当中count_one_m=f(m)-f(m-1),也就是代表数字m本身包括1的个数。

剪枝算法CalculationTimes()函数。python版代码例如以下 :

  1. def CalculationTimes(number, weight, count_one, count, table):
  2. if weight == 0:
  3. count += count_one
  4. if number == count:
  5. print "f(", number, ") = ", number
  6. return count
  7. L=10**weight
  8. maxcount = count + table[weight - 1]
  9. maxcount += count_one*L
  10. if count > (number + L -1):
  11. return maxcount
  12. if maxcount < number:
  13. return maxcount
  14. L /= 10
  15. for i in range(10):
  16. if i == 1:
  17. count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)
  18. elif i == maxcount/n - 9:
  19. return maxcount
  20. else:
  21. count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)
  22. return count

当中。相应前面剪枝算法思想。这里number代表m。weight代表步长L=10^(s-1) 中的s-1,count_one表示数n中各个位置上1的个数,比方n=112,这count_one=2,table代表gTable[]。count代表f(m-1)。详细解释例如以下:

  1. if weight == 0:
  2. count += count_one
  3. if number == count:
  4. print "f(", number, ") = ", number
  5. return count

此部分代码推断当前步长L若为1(L=10^weight)计算f(number)。f(number)通过count+=count_one实现。这是因为在步长L=1前提下,f(number)等于f(number-1)加上数字mumber本身1的个数count_one,既f(number)=f(number-1)+count_one,若f(number)=number。则输出。否则返回当f(number)。

  1. L=10**weight
  2. maxcount = count + table[weight - 1]
  3. maxcount += count_one*L
  4. if count > (number + L -1):
  5. return maxcount
  6. if maxcount < number:
  7. return maxcount

此部分代码功能运行剪枝操作。首先设置步长L=10^weight。依据性质3得f(number+L-1)=count+table[weight-1]+count_one*L。然后依据性质1推断能否够剪枝,若符合条件直接忽略(number-1。number+L-1)范围内数字并跳转到number+L再计算。

  1. L /= 10
  2. for i in range(10):
  3. if i == 1:
  4. count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)
  5. elif i == maxcount/n - 9:
  6. return maxcount
  7. else:
  8. count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)
  9. return count

若不满足剪枝条件运行此部分代码。首先依据前面描写叙述的剪枝算法总体思想将步长缩短L=L/10,然后依据性质2仅需递归计算(number-1,f(number+L-1))范围内数就可以。当i==1时,number+i*L这个数本身将多出一个”1“须要将count_one值加1,当i==maxcount/n-9时,number+i*L将超出范围终止计算。

最后,给出总体实现代码。

  1. # -*- coding:utf-8 -*-
  2. # 问题:写一个函数。计算4 000 000 000 以内的最大的那个f(n)=n的值,
  3. # 函数f的功能是统计全部0到n之间全部含有数字1的数字和。
  4.  
  5. 比方:f(13)= 6
  6. # 由于“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的总数是6(1,10,11,12,13)。
  7.  
  8. # by chasdmeng
  9. import time
  10. def GetTimes(n):
  11. temp = n
  12. temp2 =1
  13. ret = 0
  14. i = 1
  15. while temp/i:
  16. ret +=(((temp/i-1)/10)+1)*i
  17. if(((temp/i)%10) == 1):
  18. ret -=i
  19. ret +=temp2
  20. i*=10
  21. temp2=n%i+1
  22. return ret
  23. def TimesTable(n):
  24. return [GetTimes(10**(i+1) - 1) for i in xrange(n)]
  25. def CountOne(n):
  26. count = 0
  27. while n:
  28. if (n%10) == 1:count +=1
  29. n /=10
  30. return count
  31. def CalculationTimes(number, weight, count_one, count, table):
  32. if weight == 0:
  33. count += count_one
  34. if number == count:
  35. print "f(", number, ") = ", number
  36. return count
  37. L=10**weight
  38. maxcount = count + table[weight - 1]
  39. maxcount += count_one*L
  40. if count > (number + L -1):
  41. return maxcount
  42. if maxcount < number:
  43. return maxcount
  44. L /= 10
  45. for i in range(10):
  46. if i == 1:
  47. count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)
  48. elif i == maxcount/n - 9:
  49. return maxcount
  50. else:
  51. count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)
  52. return count
  53.  
  54. if __name__ == '__main__':
  55. n = 0
  56. weight = 0
  57. count = 0
  58. count_one = 0
  59. gMAX = 4000000000L
  60. start = time.time()
  61. table = TimesTable(10)
  62. while n < gMAX:
  63. count = CalculationTimes(n, weight, count_one , count, table)
  64. L = 10**weight
  65. n += L
  66. if (n/L)/10 == 1: weight +=1
  67. count_one = CountOne(n)
  68. end = time.time()
  69. print "time:",end - start

by       chasdmeng

參考文献:

博客:http://blog.csdn.net/livelylittlefish/article/details/2768348

书籍:《程序猿面试宝典(第4版)》P240。面试例题5

版权声明:本文博客原创文章。博客,未经同意,不得转载。

计算4000000000内的最大f(n)=n值---字符串的问题python实现(五岁以下儿童)的更多相关文章

  1. 设计与实现的简单和经常使用的权限系统(五岁以下儿童):不维护节点的深度level,手工计算level,树形结构

     以这种方式.和第三的类似介绍.所不同的是.深度未在数据库中存储节点level,添加和更改时间,护.而是,在程序中,实时去计算的. 至于后面的,依照level升序排序,再迭代全部的节点构造树,与第三篇 ...

  2. Python第五章-内置数据结构01-字符串

    Python 内置的数据结构 ​ 到目前为止,我们如果想保存一些数据,只能通过变量.但是如果遇到较多的数据要保存,这个时候时候用变量就变的不太现实. ​ 我们需要能够保存大量数据的类似变量的东东,这种 ...

  3. 计算给定多项式在给定点X处的值

    //计算多项式求值 //计算多项式求值#include<iostream>#include<ctime>#include<cmath>using namespace ...

  4. Python进阶(五)----内置函数Ⅱ 和 闭包

    Python进阶(五)----内置函数Ⅱ 和 闭包 一丶内置函数Ⅱ ####内置函数#### 特别重要,反复练习 ###print() 打印输入 #sep 设定分隔符 # end 默认是换行可以打印到 ...

  5. 选择排序是外面循环的array[i]与内循环的array[j]比较。冒泡排序是内循环的相邻两个值做比较修改

    选择排序是外面循环的array[i]与内循环的array[j]比较.冒泡排序是内循环的相邻两个值做比较修改

  6. 计算某个目录下所有文件的MD5值

    #!/usr/bin/env python #-*- coding:utf-8 -*- ''' 计算某个目录下所有文件的MD5值 ''' import os import sys import has ...

  7. 计算 byte[] 转 int modebus 指定位数 获取值 使用

    计算 byte[] 转 int modebus 指定位数 获取值 使用 if (bytetores.Length > 6) { int total = 0; for (int i = 0; i ...

  8. python计算非内置数据类型占用内存

    getsizeof的局限 python非内置数据类型的对象无法用sys.getsizeof()获得真实的大小,例: import networkx as nx import sys G = nx.Gr ...

  9. OpenACC 计算构建内的自定义函数

    ▶ 使用 routine 构件创建的自定义函数,在并行调用上的差别 ● 代码,自定义一个 sqab 函数,使用内建函数 fabsf 和 sqrtf 计算一个矩阵所有元素绝对值的平方根 #include ...

随机推荐

  1. Hibernate学习笔记(1)Hibernate构造

    一 准备工作 首先,我们将创建一个简单的基于控制台(console-based)Hibernate应用. 我们所做的第一件事就是创建我们的开发文件夹.并把所有需要用到的Java件放进去.解压缩从Hib ...

  2. 【转】HLSL基础

    原文地址http://blog.csdn.net/chpdirect1984/article/details/1911622 目录 前言 1.HLSL入门 1.1什么是着色器 1.2什么是HLSL 1 ...

  3. 在线API大全

    之前整理过几个经常使用api地址,在经常使用在线API集合博文中. 近期浏览网页的时候,又发现一个很完整的api的大全,分享出来,建议大家收藏起来,用的时候方便查询. 经常使用API文档索引http: ...

  4. String、StringBuffer和StringBuilder

    一.String和StringBuffer String类型和StringBuffer类型的主要性能差别事实上在于String是不可变的对象,因此在每次对String类型进行改变的时候事实上都等同于生 ...

  5. Windows Server 2012启用Windows功能NetFx3时出错解决方法

    作者:冰点阳光 | 可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址:http://baohua.me/operating-system/windows-server-2012- ...

  6. Spark第一个研究笔记1一片 - Spark一个简短的引论

    该公司推出的在线项目Spark拥有近1随着时间的推移.有效,Spark事实上,优秀的分布式计算平台,以提高生产力. 开始本篇笔记.此前的研究会Spark研究报告共享出来(由于篇幅的限制,它将被划分成制 ...

  7. fragment 中利用spinner实现省市联动

    (1)布局文件就不在说明了,主要说代码的实现,先把代码贴上! package com.example.cl; import android.annotation.SuppressLint; impor ...

  8. List subList()的一个demo

    只要保证toIndex不大于size并且fromIndex不大于toIndex即可(谁会传一个负数的下标呢?) public List<E> subList(int fromIndex, ...

  9. Cocos2d-x Auto-batching 浅浅的”深入分析”

    Auto-batching是Cocos2d-x3.0新增的特性,目的是为了代替SpriteBatchNode,完毕渲染的批处理,提高绘制效率. 至于它有什么特点,能够看看官方文档,这里主要想探讨Aut ...

  10. Oracle压缩总结2— 估计表压缩效应

    使用压缩前,我们可以估算压缩能有多大效果. 11gr2我已经能够使用dbms_comp_advisor,具体代码见附件.只需要运行两个文件dbmscomp.sql和prvtcomp.plb.然后使用D ...