计算4000000000以内最大的f(n)=n的值---字符串问题python实现(五)
問題:
? ? ? ?寫一個函數(shù),計算4 000 000 000 以內(nèi)的最大的那個f(n)=n的值,函數(shù)f的功能是統(tǒng)計所有0到n之間所有含有數(shù)字1的數(shù)字和。比如:f(13)= 6,因為“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的總數(shù)是6(1,10,11,12,13)。
分析:
? ? ? ?一、簡單算法 — 枚舉
? ? ? ?采用“枚舉法”對每個數(shù)都計算一遍1的個數(shù),直到枚舉完給定范圍所有數(shù),找到符合f(n)=n的數(shù)。此方法,代碼效率極低,運算所需時間巨大。
python版本代碼如下:
# -*- coding:utf-8 -*- # 問題:寫一個函數(shù),計算4 000 000 000 以內(nèi)的最大的那個f(n)=n的值, # 函數(shù)f的功能是統(tǒng)計所有0到n之間所有含有數(shù)字1的數(shù)字和。比如:f(13)= 6, # 因為“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的總數(shù)是6(1,10,11,12,13)。 # by chasdmeng import time def Calculation_One_Times(n):res = 0while n > 0:if n == 1:res +=1n /=10return resif __name__ == '__main__':times = 0i = 0gMAX = 4000000000Lstart = time.time()while i < gMAX:times +=Calculation_One_Times(i)if times ==i:print "f(", i, ") = ", timesi +=1end = time.time()print "time:",end - start ? ? ? ? 二、高效算法 — 剪枝
? ? ? ?主要采用剪枝算法對代碼執(zhí)行效率進行優(yōu)化。求解MAX(f(n)=n)具體算法構(gòu)建分析如下:
? ? ? ?1、求數(shù)字“0~n”中“1”的個數(shù)
? ? ? ?基本思想:
? ? ? ?t位數(shù)字n,求0到n所有數(shù)字“1”的數(shù)字和,可對1到t位(1對應(yīng)個位),每個位置上“1”出現(xiàn)的次數(shù)分別統(tǒng)計,然后求和。
? ? ? ?假設(shè),0到n之間的自然數(shù)范圍內(nèi),統(tǒng)計第m位為“1”的個數(shù)和,可以分四步驟。第一步驟,假設(shè)第二、三步驟的前提條件為第1到第m-1位內(nèi)各位數(shù)字取固定值a。第二步驟,求大于m位的數(shù)字中有多少個數(shù)字第m位為“1”。第三步驟,求等于m位的數(shù)字中有多少個數(shù)字第m位為“1”。第四步驟,把第1到第m-1位內(nèi)各位數(shù)字變化考慮進去。如下圖所示。
? ? ? ?在第二步驟中,需要分兩種情況考慮:
? ? ? (a)給定數(shù)n的第m位非0
? ? ? ?大于m位的數(shù)字中第m位為“1”的數(shù)字個數(shù)是n/10^m。
????????(b)給定數(shù)n的第m位為0
????? ?對于m位的數(shù)字中第m位為“1”的數(shù)字個數(shù)為(n/10^m)-1。這里舉例說明,n=300,m=2,由于300的第2位為0,此時n/10^m=3,若按(a)中方法求解得出在第1位固定取a 的條件下(第一步驟中規(guī)定的)大于2位的數(shù)字中第2位為“1”的數(shù)字個數(shù)是3,這顯然是錯誤的,因為在0到300中這樣的數(shù)只有“11a”、“21a”,此時正確個數(shù)應(yīng)該是n/10^m-1=2。
????? (c)合并考慮
???????大于m位的數(shù)字中第m位為“1”的數(shù)字個數(shù)為(n/(10^(m-1))-1)/10。構(gòu)造了一個通用公式可以包含(a)、(b)兩種情況。原理是,n/10^m=(n/(10^(m-1)))/10,由于是對n做除是向下取整運算,在給定數(shù)n第m位非0情況下,n/10^m=(n/(10^(m-1)))/10=(n/(10^(m-1))-1)/10;而當(dāng)給定數(shù)n第m位為0情況下,(n/10^m)-1=(n/(10^(m-1))-1)/10。可以舉具體數(shù)字對其驗證。
???????在第三步驟中,由于在第一步驟假定的前提條件,滿足求等于m位的數(shù)字中第m位為“1”的個數(shù)只有1個。這里舉例說明,n=300,m=2,2位數(shù)字中第2位為“1”的數(shù)僅有“1a”。
? ? ? ?在第四步驟中,綜合第一、二、三步驟后,在第1到第m-1位內(nèi)各位數(shù)字取固定值a的條件下,第m位為“1”的個數(shù)和為(n/(10^(m-1))- 1)/10+1。下面把第1到第m-1位內(nèi)各位數(shù)字變化考慮進去,由于在第1到第m-1位中每一位可以放0~9之間任意數(shù),m-1位數(shù)共有“10^m-1”個。最終,0到n之間的自然數(shù)范圍內(nèi),第m位為“1”的個數(shù)和((n/(10^(m-1))- 1)/10+1)*(10^(m-1))。
? ? ? ?小結(jié):對以上分析結(jié)果給予公式化定義。Cm=((n/i - 1)/10 + 1)*i,其中i=10^(m-1),n為任意自然數(shù),Cm為0到n的數(shù)中第m位為1 的 個數(shù)和。
? ? ? ?求數(shù)字“0~n”中“1”個數(shù)的GetTimes()函數(shù),python版代碼如下:
def GetTimes(n):temp = ntemp2 =1ret = 0i = 1while temp/i:ret +=(((temp/i-1)/10)+1)*iif(((temp/i)%10) == 1):ret -=iret +=temp2i*=10temp2=n%i+1return ret
? ? ? ?2、從0到n位最大數(shù)之間1的總個數(shù),尋找數(shù)學(xué)規(guī)律
? ? ? ?當(dāng)n=1,2,3,...,6,...時,分別計算從0到n位最大數(shù)之間1的總個數(shù),部分?jǐn)?shù)據(jù)如下:
? ? ? ?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
? ? ? ?… ? ? ? ? ? ? ? ? ??…
? ? ? ?從中,可以發(fā)現(xiàn)如下規(guī)律:
? ? ? ?a1 = 1
? ? ? ?a2 = 10*a1 + 10
? ? ? ?…
? ? ? ?an = 10*an - 1 + 10^(n - 1)
? ? ? ?即0~9…9(n個9)之間1的個數(shù)為an=10*an-1+10^(n-1)個。
? ? ? ?這樣,當(dāng)計算任意一個數(shù)字n的0到n之間所有數(shù)字1的個數(shù)和時,可以采用將數(shù)字n拆分成0~9、0~99... 等部分進行之間計算。例如:n=123,可將n拆分為0~99和100~123計算:(1)0~99對應(yīng)1的個數(shù)和直接可知為20;(2)百位數(shù)1的個數(shù)為23+1個;(3)剩下的0~23繼續(xù)拆分為0~9、10~19、20~23,共兩個0~9個數(shù)為2;(4)因為23中十位數(shù)2>1,十位為1的個數(shù)10;(5)現(xiàn)在就剩下20~23了,個數(shù)為1。所以,0~123中1的個數(shù)為20+24+2+10+1=57。
? ? ? ?因此,在求任意數(shù)0~n內(nèi)所有1的個數(shù)和時,可以首先建立“int ?gTable[10]”結(jié)構(gòu)用于存放a1、a2、a3、...、a9、a10,其中an表示0~n內(nèi)所有1的個數(shù),然后將數(shù)n拆分為0~9、0~99... 等部分直接使用gTable的值進行計算。這種方式可以提高算法效率。
? ? ? ?生成gTable[]結(jié)構(gòu)的TimesTable()函數(shù),python版代碼如下:
def TimesTable(n):return [GetTimes(10**(i+1) - 1) for i in xrange(n)]
? ? ? ?3、采用剪枝算法搜索0~n之間滿足f(q)=q的數(shù)
? ? ? ?由于若對0~n的所有數(shù)據(jù)進行枚舉驗證f(q)=q,當(dāng)n值很大時運算相當(dāng)費時,從本文開頭給出的“最簡單方法”中可以感受到代碼效率有多么低。為了優(yōu)化算法提高效率可采用減枝算法,其思想是對0~n之間所有數(shù)建立搜索樹,對不符合條件的搜索樹進行剪枝,這樣就可以極大縮小搜索范圍,提高算法效率。如何構(gòu)建剪枝規(guī)則呢?首先對這個問題進行數(shù)學(xué)化描述:
? ? ? ?定義1:設(shè) f(x)=y,其中x∈ (0,n),f為x與對應(yīng)的(0,x)上所有數(shù)字1的個數(shù)總和y的映射。
? ? ? ?先觀察下f(n)的特點,可以發(fā)現(xiàn)f(n)是一個單調(diào)遞增函數(shù),這樣可以歸納出它的特性:
? ? ? ?性質(zhì)1:若有f(m)=a,f(t)=b,且m<t,則存在q∈(m,t)滿足f(q)=q成立的條件是:b>m 且a< t。
? ? ? ?現(xiàn)在就可以應(yīng)用“性質(zhì)1”進行減枝操作。具體來說通過“性質(zhì)1”可知,當(dāng)對0~n的所有數(shù)據(jù)進行枚舉驗證f(q)=q時,若當(dāng)f(t)=b<m或f(m)=a>n,則在[m,t]范圍內(nèi)不存在滿足f(q)=q成立的數(shù)q,這樣就可忽略掉[m,t]內(nèi)這些自然數(shù),直接跳到n+1往后繼續(xù)枚舉。在應(yīng)用剪枝算法枚舉符合條件的f(q)=q時,是以t-m為步長在0~n內(nèi)逐段排查是否可剪枝,若t-m范圍內(nèi)不滿足剪枝條件則進行枚舉搜索。因此在進行剪枝算法時[m,t]的范圍取多大適合呢?這是需要慎重考慮的問題,合理的[m,t]取值能讓大幅提升算法執(zhí)行效率。下面就對[m,t]步長L取值進行分析。
? ? ? ?[m,t]的取值依據(jù)是能最大化提升算法效率,由于在“2、從0到n位最大數(shù)之間1的總個數(shù),尋找數(shù)學(xué)規(guī)律。”中設(shè)置了“int ?gTable[10]”結(jié)構(gòu)用于計算f(n),gTable分別表示0~9中1個數(shù)和1、0~99中1個數(shù)和20、0~999中1的個數(shù)和300...,可見是以10^p倍增長,而且f(n)還需要使用gTable去求解,因此初步考慮將[m,t]步長L=10^p,雖然[m,t]取10的倍數(shù),但p具體取多少,還需要繼續(xù)分析。
? ? ? ? 對于任意s位數(shù)m,設(shè)s位數(shù)的最大值為max(s),則f(max(s))=gTable[s-1],假定取[m,t]步長L=10^s也就是 t=m+10^s,設(shè)f(m)=a、f(m+10^s)=b,則有m<max(s)<m+10^s,由于f()為單調(diào)增函數(shù),推出gT[s-1]=f(max(s))<f(m+10^s)=b,a<gTable[s-1],又因為在0~10^10-1的范圍內(nèi)有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,因此根據(jù)性質(zhì)1可知m=d*10^s時,取[m,t]步長L=10^s恒不滿足減枝條件,同理步長L=10^(s+1)也無法滿足剪枝條件,因此只能往下取值。對于任意s位數(shù)m,取步長L<=10^(s-1)某些數(shù)內(nèi)可以滿足減枝條件,舉例來說,m=20,s=2,L=10^(s-1)=10,則在[20,30]內(nèi)f(20)=12、f(30)=13,根據(jù)性質(zhì)1可以剪枝掉[20,30]范圍內(nèi)的數(shù)。綜上所述,對任意s位數(shù)m,m∈ (0,n),剪枝算法初始步長L=10^(s-1)? ?。
? ? ? ?性質(zhì)2:若有f(m)=a,f(t)=b,且m<t,滿足b>m 且a< t時,有f(q)=q,且q∈(m,b]。
? ? ? ?性質(zhì)2是由性質(zhì)1推到而來,性質(zhì)2中將原來性質(zhì)1中q∈(m,t)縮小為q∈(m,b)。首先在0~10^10-1的范圍內(nèi)滿足性質(zhì)1條件下可知b∈(m,t],設(shè)f(b+1)=c,則在(b+1,t)內(nèi)f(b+1)=c、f(t)=b根據(jù)性質(zhì)1不存在q∈(b+1,t)使f(q)=q成立。所以,在(m,t)內(nèi)使f(q)=q成立的q∈(m,b]。
? ? ? ? 剪枝算法整體思想是:n位數(shù)m,步長L=10^(n-1),在區(qū)間(m-1,m+L-1)判斷是否滿足f(m-1)>boundary或f(m+L-1)<m ,滿足就直接跳到m=m+L計算,否則在區(qū)間(m-1,f(m+L-1)+L-1)上置步長L=L/10遞歸重復(fù)執(zhí)行剪枝判斷,當(dāng)遞歸到步長L=1時,進行逐值枚舉f(number)是否等于number。由于算法中m是從0開始枚舉取值,以10的倍數(shù)遞增,因此所有的m都應(yīng)滿足abc*10^s格式,也就是說,m取到的是10的倍數(shù)。因此有如下性質(zhì)。
? ? ? ?性質(zhì)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),也就是代表數(shù)字m本身包含1的個數(shù)。
? ? ? ?剪枝算法CalculationTimes()函數(shù),python版代碼如下 :?
def CalculationTimes(number, weight, count_one, count, table):if weight == 0:count += count_oneif number == count:print "f(", number, ") = ", numberreturn countL=10**weightmaxcount = count + table[weight - 1]maxcount += count_one*Lif count > (number + L -1):return maxcountif maxcount < number:return maxcountL /= 10for i in range(10):if i == 1:count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)elif i == maxcount/n - 9:return maxcountelse:count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)return count
? ? ? ?其中,對應(yīng)前面剪枝算法思想,這里number代表m,weight代表步長L=10^(s-1)?中的s-1,count_one表示數(shù)n中各個位置上1的個數(shù),比如n=112,這count_one=2,table代表gTable[],count代表f(m-1)。具體解釋如下:
if weight == 0:count += count_oneif number == count:print "f(", number, ") = ", numberreturn count 此部分代碼判斷當(dāng)前步長L若為1(L=10^weight)計算f(number),f(number)通過count+=count_one實現(xiàn),這是由于在步長L=1前提下,f(number)等于f(number-1)加上數(shù)字mumber本身1的個數(shù)count_one,既f(number)=f(number-1)+count_one,若f(number)=number,則輸出,否則返回當(dāng)f(number)。
L=10**weightmaxcount = count + table[weight - 1]maxcount += count_one*Lif count > (number + L -1):return maxcountif maxcount < number:return maxcount
此部分代碼功能執(zhí)行剪枝操作,首先設(shè)置步長L=10^weight,根據(jù)性質(zhì)3得f(number+L-1)=count+table[weight-1]+count_one*L,然后根據(jù)性質(zhì)1判斷是否可以剪枝,若符合條件直接忽略(number-1,number+L-1)范圍內(nèi)數(shù)字并跳轉(zhuǎn)到number+L再計算。
L /= 10for i in range(10):if i == 1:count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)elif i == maxcount/n - 9:return maxcountelse:count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)return count 若不滿足剪枝條件執(zhí)行此部分代碼。 首先根據(jù)前面描述的 剪枝算法整體思想 將步長縮短L=L/10,然后 根據(jù)性質(zhì)2僅需遞歸計算( number-1,f(number+L-1)) 范圍內(nèi)數(shù)即可,當(dāng)i==1時,number+i*L這個數(shù)本身將多出一個”1“需要將count_one值加1,當(dāng)i==maxcount/n-9時,number+i*L將超出范圍終止計算。
? ? ? ?最后,給出整體實現(xiàn)代碼。
# -*- coding:utf-8 -*- # 問題:寫一個函數(shù),計算4 000 000 000 以內(nèi)的最大的那個f(n)=n的值, # 函數(shù)f的功能是統(tǒng)計所有0到n之間所有含有數(shù)字1的數(shù)字和。比如:f(13)= 6, # 因為“1”在“1,2,3,4,5,6,7,8,9,10,11,12,13”中的總數(shù)是6(1,10,11,12,13)。 # by chasdmeng import time def GetTimes(n):temp = ntemp2 =1ret = 0i = 1while temp/i:ret +=(((temp/i-1)/10)+1)*iif(((temp/i)%10) == 1):ret -=iret +=temp2i*=10temp2=n%i+1return ret def TimesTable(n):return [GetTimes(10**(i+1) - 1) for i in xrange(n)] def CountOne(n):count = 0while n:if (n%10) == 1:count +=1n /=10return count def CalculationTimes(number, weight, count_one, count, table):if weight == 0:count += count_oneif number == count:print "f(", number, ") = ", numberreturn countL=10**weightmaxcount = count + table[weight - 1]maxcount += count_one*Lif count > (number + L -1):return maxcountif maxcount < number:return maxcountL /= 10for i in range(10):if i == 1:count = CalculationTimes(number + i*L, weight - 1, count_one + 1, count, table)elif i == maxcount/n - 9:return maxcountelse:count = CalculationTimes(number + i*L, weight - 1, count_one , count, table)return countif __name__ == '__main__':n = 0weight = 0count = 0count_one = 0gMAX = 4000000000Lstart = time.time()table = TimesTable(10)while n < gMAX:count = CalculationTimes(n, weight, count_one , count, table)L = 10**weightn += Lif (n/L)/10 == 1: weight +=1count_one = CountOne(n)end = time.time()print "time:",end - start? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?by ? ? ? chasdmeng
參考文獻:
? ? ? 博客:http://blog.csdn.net/livelylittlefish/article/details/2768348
? ? ? 書籍:《程序員面試寶典(第4版)》P240,面試?yán)}5
總結(jié)
以上是生活随笔為你收集整理的计算4000000000以内最大的f(n)=n的值---字符串问题python实现(五)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 音频分类-数据集:Urbansound8
- 下一篇: 囧之后 网络上“杯具”流行