《程式設計之美》學習筆記 2 4 1的數目

2021-06-27 21:48:05 字數 4549 閱讀 7709

一、問題

給定乙個十進位制正整數n,統計從1開始,到n(含n)的所有整數中出現的所有「1」(包含各個位)的個數。

二、解法

版本一:最簡單的思路,就是從1到n進行遍歷,統計逐個數上「1」的個數並相加,最後的結果就是所求的值。

9 #include 10 #include 11 

12 typedef int type;

13 type n = 9999;

14 15 type count_number_1(type n)

16 22 return sum;

23 }

24 25 int main(void)

26 32 printf("sum of number 1 in %d is %d.\n", n, sum);

33 return exit_success;

34 }

特點:逐個數遍歷,並利用求餘演算法統計各個位上「1」的總數。

時間複雜度計算:o(nlogn)   (對數函式實際上以10為底)

(關於對數時間計算參考維基百科:時間複雜度 對數時間:若演演算法的t(n) = o(log n),則稱其具有對數時間。由於計算機使用二進位制的記數系統,對數常常以2為底(即log2 n,有時寫作lg n)。然而,由對數的換底公式,loga n和logb n只有乙個常數因子不同,這個因子在大o記法中被丟棄。因此記作o(log n),而不論對數的底是多少,是對數時間演演算法的標準記法。)

gprof 時間效能監視測試:

版本二:分析數學規律,思考「小數n在每一位上可能出現1的次數」之和來得到結果。

9 #include 10 #include 11 

12 typedef int type;

13 type n = 1000000000;

14 15 type main(void)

16

39 printf("sum of number 1 in %d is %d.\n", n, sum);

40 return exit_success;

41 }

特點:考慮了1-n各個數字之間的關係,對於不同的位數bits的數,可以通過統計每個位上的「1」的次數來求解,這需要找到他們的數學規律,可以採用歸納法來求解。這道題是先把問題轉化為數學問題思考,再轉回程式設計實現。下面為思考過程(對於不同位數通過找例子測試得到規律)

對於一位數a(a >= 0):

個位數上的1統計值:

a < 1: a = 0

a >= 1: a = 1

「1」的個數:a

對於二位數ba(b >= 1):

個位數上的1統計值:

a < 1: a = b

a >= 1: a = b + 1

十位數上的1統計值:

b = 1: b = a + 1

b > 1: b = 10

「1」的個數:a + b

對於三位數cba(c >= 1):

個位數上的1統計值:

a < 1: a = cb

a >= 1: a = cb + 1

十位數上的1統計值:

b = 0: b = 10 * c

b = 1: b = 10 * c + a + 1

b > 1: b = 10 * (c + 1)

百位數上的1統計值:

c = 1: ba + 1

c > 1: 100

「1」的個數:a + b + c

對於四位數dcba(d >= 1):

個位數上的1統計值:

a < 1: a = dcb

a >= 1: a = dcb + 1

十位數上的1統計值:

b = 0: b = 10 * dc

b = 1: b = 10 * dc + a + 1

b > 1: b = 10 * (dc + 1)

百位數上的1統計值:

c = 0: c = 100 * d

c = 1: c = 100 * d + ba + 1

c > 1: c = 100 * (d + 1)

千位數上的1統計值:

d = 1: d = cba + 1

d > 1: d = 1000

「1」的個數:a + b + c + d

對於五位數edcba(e >= 1):

個位數上的1統計值:

a = 0: a = 1 * edcb

a = 1: a = 1 * edcb + k + 1 (k = 0)

a > 1: a = 1 * (edcb + 1)

十位數上的1統計值:

b = 0: b = 10 * edc

b = 1: b = 10 * edc + a + 1

b > 1: b = 10 * (edc + 1)

百位數上的1統計值:

c = 0: c = 100 * ed

c = 1: c = 100 * ed + ba + 1

c > 1: c = 100 * (ed + 1)

千位數上的1統計值:

d = 0: c = 1000 * e

d = 1: c = 1000 * e + cba + 1

d > 1: c = 1000 * (e + 1)

萬位數上的1統計值:

e = 0: x (不存在)

e = 1: d = 1000 * f + dcba + 1 (f = 0)

e > 1: d = 10000 * (f + 1) (f = 0)

「1」的個數:a + b + c + d + e

時間複雜度計算:o(logn)(對數函式實際上以10為底)

版本三:逐步縮小資料規模,把大的數分解為更小的數的組合,利用遞迴的思想求解。

(此想法參考自一篇文章:

9 #include 10 #include 11     

12 typedef long long type;

13 type n = 1000000000;

14

15 type count_number_1(type n)

16

26 pow_bits /= 10;

27 if(bits > 1) else

40 } else

47 return sum;

48 }

49

50 int main(void)

51

時間複雜度計算:o(2^logn)(對數函式實際上以10為底)

gprof 時間效能監視測試:

特點:考慮的資料規模的遞減,通過遞迴思路不斷縮小資料規模,最後只剩下個位數「1」含有個數的求解。但是資料過大時遞迴可能會導致堆疊溢位。下面為思考過程(對於不同位數通過找例子測試得到規律):

個位數a:f(a)

a < 1 : a = 0

a >= 1 : a = 1

十位數ba:

b = 1 : ba = f(a) + f(9) + a + 1

b > 1 : ba = f(a) + b * f(9) + 10

百位數cba:

c = 1 : cba = f(ba) + f(99) + ba + 1

c > 1 : cba = f(ba) + c * f(99) + 100

千位數dcba:

d = 1 : cba = f(cba) + f(999) + cba + 1

d > 1 : cba = f(cba) + d * f(999) + 1000

三、總結與思考

這道題目的第一種解法是最先想到的解法,符合程式設計的思維習慣,但是第二種解法確使運算效率提公升到乙個新的檔次,第二種解法,從數學的思考角度去考慮,通過尋找規律來尋找求解。所以,有的複雜的演算法,先轉變為數學問題,通過數學規律或相互關係尋找新的數學上的解法,再程式設計實現,效率說不定能夠快速提公升。當然,第一種解法直觀易懂,大家很容易推出問題,第二種解法不容易推出問題,但是效率確實更優的。當程式運算效率跟不上時,不防通過考慮下數學問題推導這種思維進行優化。

第三種解法參考別的文章的提示思路,採用的時遞迴縮小資料規模的想法,運用了分治的思想,這個方法實際測試效率時發現也效率和是相當高的,但時間複雜度這個值參考別人的計算結果,並未自己驗證,因此無法準確的進行評估。

3.有寫錯或寫漏之處請指正,謝謝!

程式設計之美2 4 1的數目

總體思路 先求個位上出現的1的個數,再找十位再找百位。先看個位找找規律 5 05 1 1 15 2 1,11 25 3 1,11,21 35 4 1,11,21,31 325 33 1,11,21,31,41,301,311,321 結論 個位上的1的數目s n 10 1 再看十位,找找規律 25 ...

程式設計之美 2 4 「1的數目」

擴充套件問題 二進位制數n,從1開始,到n的所有二進位制數,然後數下其中出現的所有 1 的個數。例如 f 1 1 f 10 2 因為01,10共有2個1 f 11 100 因為01,10,11共有4個1 思路 以10110為例,考慮最高位為0時共有幾個1,因為後面4位中1和0出現的概率是相等的,也即...

程式設計之美 2 4 1的數目

package code.beauty.fungame public class countone private static int count1inaint int n return num private static void count1inallint int n system.out...