計算0到N中包含數字1的個數(整合)

2021-07-27 13:33:34 字數 3834 閱讀 8778

有這樣乙個函式f(n),對於任意正整數n,它表示從 0 到 n 之間出現「1」的個數,比如 f(1) = 1, f(13) = 6,請列出從 1 到 1234567890 中所有的 f(n) = n 的n, 要求準確快速.

相信很多人都能立刻得出以下的解法:

for(n:n)

這是最直接的解法,但遺憾的是,時間複雜程度為o(n*logn)。因為還需要迴圈判斷當前的n的各位數,該判斷的時間複雜程度為o(logn)。

接下來就應該思考效率更高的解法了。說實話,這道題讓我想起另外一道簡單的演算法題:

n為正整數,計算從1到n的整數和。

很多人都採用了迴圈求解。然後利用初等數學知識就知道s=n*(n+1)/2,所以用o(1)的時間就可以處理。

再回到本道題目,同理應該去尋找到結果r與n之間的對映關係。

分析如下:

假設n表示為a[n]a[n-1]…a[1],其中ai表示n的各位數上的數字。

c[i]表示從整數1到整數a[i]…a[1]中包含數字1的個數。

x[i]表示從整數1到10^i - 1中包含數字1的個數,例如,x[1]表示從1到9的個數,結果為1;x[2]表示從1到99的個數,結果為20;

當a[1]=0時,c[1] = 0;

當a[1]=1時,c[1] = 1;

當a[1]>1時,c[1] = 1;

當a[2]=1時,c[2] = a[1] +1+ c[1] + x[1];

當a[2]>1時,c[2] = a[2]*x[1]+c[1]+10;

當a[3]=1時,c[3] = a[2]*a[1] +1+ c[2] + x[2];

當a[3]>1時,c[3] = a[3]*x[2]+c[2]+10^2;

以此類推

當a[i]=1時,c[i] = a[i-1]*…*a[1] +1+ c[i-1]+x[i-1];

當a[i]>1時,c[i] = a[i]x[i-1]+c[i-1]+10^(i-1);

解法一:

給定乙個十進位制正整數 n,寫下從 1 開始,到 n 的所有整數,

然後數一下其中出現的所有「1」的個數。

例如:

n= 2,寫下 1,2。這樣只出現了 1 個「1」。

n= 12,我們會寫下 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12。這樣,1的個數是 5。

問題是:

寫乙個函式f(n) 返回1到n之間出現的「1」的個數,比如f(12)=5。

這個問題看上去並不是乙個困難的問題,因為不需要太多的思考,大家都能找到乙個最簡單的方法來計算 f(n),那就

是從1開始遍歷到n,將其中每乙個數中含有「1」的個數加起來,

自然就得到了從1到n所有「1」的個數的和.(**見最後)

但是這個演算法的致命問題是效率,它的時間複雜度是o(n)×計算乙個整數數字裡面「1」的個數的複雜度 = o(n *log2 n),如果給定的 n 比較大,則需要很長的運算時間才能得到計算結果。我試著輸入100000000,一共用了大概12秒,貌似有點長,不夠比作者說的40s快多了,作者的電腦有點舊…….- .-

解法二先從一些簡單的情況開始觀察:

如果n是一位數,可以確定f(n)=1

如過是二位數,如果 n=13,那麼從 1 到 13 的所有數字:1、2、3、4、5、6、

7、8、9、10、11、12、13,個位和十位的數字上都可能有 1,我們可以將它們分開來考慮,個位出現 1 的次數有兩次:1 和 11,十位出現 1 的次數有 4 次:10、11、12 和 13,所以 f(n)=2+4=6。要注意的是 11 這個數字在十位和個位都出現了 1, 但是 11 恰好在個位為 1 和十位為 1 中被計算了兩次,所以不用特殊處理,是對的。再考慮 n=23 的情況,它和 n=13 有點不同,十位出現 1 的次數為 10 次,從 10 到 19,個位出現 1 的次數為 1、11 和 21,所以f(n)=3+10=13。通過對兩位數進行分析,我們發現,個位數出現 1 的次數不僅和個位數字有關,還和十位數有關:如果 n 的個位數大於等於 1,則個位出現 1 的次數為十位數的數字加 1;如果n 的個位數為 0,則個位出現 1 的次數等於十位數的數字。而十位數上出現 1 的次數不僅和十位數有關,還和個位數有關:如果十位數字等於 1,則十位數上出現 1 的次數為個位數的數字加 1;如

果十位數大於 1,則十位數上出現 1 的次數為 10。

f(13) = 個位出現1的個數 + 十位出現1的個數 = 2 + 4 = 6;

f(23) = 個位出現1的個數 + 十位出現1的個數 = 3 + 10 = 13;

f(33) = 個位出現1的個數 + 十位出現1的個數 = 4 + 10 = 14;

… f(93) = 個位出現1的個數 + 十位出現1的個數 = 10 + 10 =

20;接著分析 3 位數,

如果 n = 123:

個位出現 1 的個數為 13:1, 11, 21, …, 91, 101, 111, 121

十位出現 1 的個數為 20:10~19, 110~119

百位出現 1 的個數為 24:100~123

f(23)= 個位出現 1 的個數 + 十位出現 1 的個數 + 百位出現 1 的次數 = 13 + 20 + 24 = 57;同理我們可以再分析 4 位數、 位數。 根據上面的一些嘗試,下面我們推導出一般情況下,從 n 得

到 f(n)的計算方法: 假設 n=abcde,這裡 a、b、c、d、e 分別是十進位制數 n 的各個數字上的數字。如果要計算百位上出現 1 的次數,它將會受到三個因素的影響:百位上的數字,百位以下(低位)的數字,百

位(更高位)以上的數字。如果百位上的數字為 0,則可以知道,百位上可能出現 1 的次

數由更高位決定,比如 12 013,則可以知道百位出現 1 的情況可能

是 100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,

一共有 1 200 個。也就是由更高位數字(12)決定,並且等於更高

位數字(12)×當前位數(100)。

如果百位上的數字為 1,則可以知道,百位上可能出現 1 的次數不僅受更高位影響,還受低位影響,也就是由更高位和低位共同決定。例如對於 12 113,受更高位影響,百位出現 1 的情況是 100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共 1 200個,和上面第一種情況一樣,等於更高位數字(12)×當前位數(100)。但是它還受低位影響,百位出現 1 的情況是 12 100~12 113,一共14 個,等於低位數字(13)+1。 如果百位上數字大於 1(即為 2~9),則百位上可能出現 1的次數也僅由更高位決定,比如 12 213,則百位出現 1 的可能性為:100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,12 100~12 199,一共有 1 300 個,並且等於更高位數字+1(12+1)

×當前位數(100)。通過上面的歸納和總結,我們可以寫出如下的更高效演算法來。

計算 f(n):

#include "stdio.h"

#include "stdlib.h"

int count1(int n);

int count2(int n);

int main(void)

//解法一

int count1(int n)

} return count;

}//解法二:

int count2(int n)

factor *= 10;

} return count;

}

此法通過clock()的計時函式做比較,在輸入100000000速度至少提高10000倍。若輸入為4 000 000 000,則需要將程式中的型別 int 都更換為型別long int,以及對應輸出模式改變後,即可執行。

計算1到N中包含數字1的個數

mark n為正整數,計算從1到n的所有整數中包含數字1的個數。比如,n 10,從1,2.10,包含有2個數字1。相信很多人都能立刻得出以下的解法 for n n 這是最直接的解法,但遺憾的是,時間複雜程度為o n logn 因為還需要迴圈判斷當前的n的各位數,該判斷的時間複雜程度為o logn 接...

計算N 末尾所包含0的個數

問題描述 給定乙個整數n,求出n 末尾有多少個0?計算公式 令f x 表示正整數x末尾所包含0的個數,則有 當0 當n 5時,f n k f k 其中k n 5 取整 公式推導 證明之前先熟悉乙個結論。結論 對於n 其因式分解中,如果存在乙個因子 5 那麼它必然對應n 末尾的乙個 0 下面證明這個結...

計算n 末尾所包含0的個數

例如,5!120,其末尾所含有的 0 的個數為1 10!3628800,其末尾所含有的 0 的個數為2 20!2432902008176640000,其末尾所含有的 0 的個數為4。這裡先給出其計算公式,後面給出推導過程。令f x 表示正整數x末尾所含有的 0 的個數,則有 當0 n 5時,f n ...