一本書的頁碼從自然數1開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排,每個頁碼都不含多餘的前導數字0。例如第6頁用6表示而不是06或006。數字統計問題要求對給定書的總頁碼,計算出書的全部頁碼中分別用到多少次數字0,1,2,3,.....9。
這個題目有個最容易想到的n*log10(n)的演算法。這是自己寫的複雜度為o(n*log10(n))的**:
void statnumber(int n) ;
for(i = 1; i <= n; i++)
} for(i = 0; i < 10; i++)
}仔細考慮m個n位十進位制數的特點,在乙個n位十進位制數的由低到高的第i個數字上,總是連續出現10^i個0,然後是10^i個1……一直到10^i個9,9之後又是連續的10^i個0,這樣迴圈出現。找到這個規律,就可以在常數時間內算出第i個數字上每個數字出現的次數。而在第i個數字上,最前面的10^i個0是前導0,應該把它們減掉。
這樣,可以只分析給定的輸入整數n的每個數字,從面可以得到乙個log10(n)的演算法,**如下:
void statnumber(int n) , count[10] = ;
for(i = 1; i < 12; i++)
sprintf(d, "%d", n);
m = n+1;
for(i = 0; i <= len; i++)
while(j < 10)
count[0] -= pow10[len-i]; /* 第i個數字上前10^i個0是無意義的 */
} for(j = 0; j < 10; j++)
}通過對隨機生成的測試資料的比較,可以驗證第二段**是正確的。
對兩段**做效率測試,第一次隨機產生20萬個整數,結果在我的電腦上,第二段**執行1.744秒。第一段**等我吃完鈑回來看還是沒反應,就強行關了它。
第二次產生了1000個整數,再次測試,結果第一段**在我的電腦上執行的時間是
10.1440秒,而第二段**的執行時間是0.0800秒。
其原因是第一段**時間複雜度為o(n*log10(n)),對m個輸入整數進行計算,則需要的時間為 1*log10(1) + 2*log10(2) + ... + m*log10(m), 當n > 10時,有
n*log10(n) > n,所以上式的下界為11+12+....+m,其漸近界為m*m。對於20萬個測試資料,其執行時間的下界就是4*10^10。
同樣可得第二段**對於n個輸入資料的執行時間界是n*log10(n)的。
上面的**中有個pow10陣列用來記錄10^i,但10^10左右就已經超過了2^32,但是題目給定的輸入整數的範圍在10^9以內,所以沒有影響。
原著中給出的分析如下:
考察由0,1,2...9組成的所有n位數。從n個0到n個9共有10^n個n位數。在這10^n個n位數中,0,1,2.....9第個數字使用次數相同,設為f(n)。f(n)滿足如下遞推式:
n>1:
f(n) = 10f(n-1)+10^(n-1)
n = 1:
f(n) =1
由此可知,f(n) = n*10^(n-1)。
據此,可從高位向低位進行統計,再減去多餘的0的個數即可。
著者的思想說的更清楚些應該是這樣:
對於乙個m位整數,我們可以把0到n之間的n+1個整數從小到大這樣來排列:
000......0
.............
199......9
200......0
299......9
.........
這樣一直排到自然數n。對於從0到199......9這個區間來說,拋去最高位的數字不看,其低m-1位恰好
就是m-1個0到m-1個9共10^(m-1)個數。利用原著中的遞推公式,在這個區間裡,每個數字出現的次數
(不包括最高位數字)為(m-1)*10^(m-2)。假設n的最高位數字是x,那麼在n之間上述所說的區間共有
x個。那麼每個數字出現的次數x倍就可以統計完這些區間。再看最高位數字的情況,顯然0到x-1這些
數字在最高位上再現的次數為10^(m-1),因為乙個區間長度為10^(m-1)。而x在最高位上出現次數就是
n%10程式設計客棧^(m-1)+1了。接下來對n%10^(m-1),即n去掉最高位後的那個數字再繼續重複上面的方法。直到
個位,就可以完成題目要求了。
比如,對於乙個數字34567,我們可以這樣來計算從1到34567之間所有數字中每個數字出現的次數:
從0到9999,這個區間的每個數字的出現次數可以使用原著中給出的遞推公式,即每個數字出現4000次。
從10000到19999,中間除去萬位的1不算,又是乙個從0000到9999的排列,這樣的話,從0到34567之間
的這樣的區間共有3個。所以從00000到29999之間除萬位外每個數字出現次數為3*4000次。然後再統計
萬位數字,每個區間長度為10000,所以0,1,2在萬位上各出現10000次。而3則出現4567+1=4568次。
之後,拋掉萬位數字,對於4567,再使用上面的方法計算,一直計算到個位即可。
下面是自己的實現**:
void statnumber_iterative(int n) ;
int pow10[12] = ;
char d[16];
len = log10(n); /* len表示當前數字的位權 */
m = len;
sprintf(d, "%d", n);
k = 0; /* k記錄當前最高位數字在d陣列中的下標 */
h = d[k] - '0'; /* h表示當前最高位的數字 */
n %= pow10[len]; /* 去掉n的最高www.cppcns.com位 */
while(len > 0)
for(i = 0; i < 1www.cppcns.com0; i++)
for(i = 0; i < h; i++)
count[h] += n + 1;
--len;
h = d[++k] - '0';
n %= pow10[len];
} for(i = 0; i <= h; i++)
/* 減去前導0的個數 */
for(i = 0; i <= m; i++)
for(i = 0; i < 10; i++)
}本文標題: 關於統計數字問題的演算法
本文位址:
關於統計數字
說起來最近做題又遇到一道有意思的題,就是統計出現整數最多,開始我想用陣列做,就和統計各位數字出現最多那種,比如 include void main int i,m,n,max 0 scanf d n for i 0 i其中 while m 就是通過陣列來統計,但是我發現有負數的情況會越界,於是我就瞎...
統計數字問題
在王曉東編著的 演算法設計與實驗題解 中看到的這個問題,問題描述如下 一本書的頁碼從自然數1開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排,每個頁碼都不含多餘的前導數字0。例如第6頁用6表示而不是06或006。數字統計問題要求對給定書的總頁碼,計算出書的全部頁碼中分別用到多少次數字0,1,2,...
統計數字問題
問題描述如下 一本書的頁碼從自然數1開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排,每個頁碼都不含多餘的前導數字0。例如第6頁用6表示而不是06或006。數字統計問題要求對給定書的總頁碼,計算出書的全部頁碼中分別用到多少次數字0,1,2,3,9。演算法設計與分析習題 分析 考察由0,1,2.9...