數字統計問題

1.問題描述
一本書的頁碼從自然數1開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排,每個頁碼都不含多餘的前導數字0。例如,第6頁用數字6表示,而不是06或006等。數字計數問題要求對給定書的總頁碼n,計算出書的全部頁碼中分別用到多少次數字0,1,2,…,9。

2.算法設計
給定表示書的總頁碼的10進制整數n(1≤n≤109)。編程計算數的全部頁碼中分別用到多少次數字0,1,2,…,9。

3.數據輸入
輸入數據由文件名爲input.txt的文本文件提供,每個文件只有1行,給出表示書的總頁碼的整數n。

4.結果輸出
將計算結果輸出到文件output.txt。輸出文件共有10行,在第k行輸出頁碼中用到數字k-1的次數,k=1,2,3,…,10。

5.求解問題的算法描述

{ 在一個n位十進制數的由低到高的第i位上,總是連續出現10^i個0, 10^i個1, ……, 10^i個9, 9之後又是連續的10^i個0, 10^i個1, ……, 10^i個9,這樣循環出現。由此可以在第i位上求出每個數字出現的次數,而在第i個數位上,最前面的10^i個0是前導0,應該在統計的時候減掉。}
以上是我百度到的一段算法解析,沒看太明白……

以下是我的分析:
我把頁碼看成是一個字符串,也就是 一個N位數 PageNum = AB…Z(其中0≤ A,B…,Z ≤9 ),比如A=a, B=b, …, C=c(0≤ a,b, … , z ≤9)

0.在A位置上:
number < a : count[number] += 10^(N-1);
number = a : count[number] += bc…z + 1;
number > a: count[number] += 0;
1.在B位置上:
number < b: count[number] += 10^(N-2) * (a+1);
number = b: count[number] += 10^(N-2) * a + (cd…z + 1);
number > b: count[number] += 10^(N-2) * a
2.在C位置上:
number < c: count[number] += 10^(N-3) * (ab+1);
number = c: count[number] += 10^(N-3) * (de…z + 1);
number > c: count[number] += 10^(N-3) * ab;
……
i.在I位置上:
number < i: count[number] += 10^(N-i-1) * (i前面的數字 + 1);
number = i: count[number] += 10^(N-i-1) * (i後面的數字 + 1);
number > i: count[number] += 10^(N-i-1) * i前面的數字;

PS:以上計算的時候並沒有討論0的情況,而是把0006中0出現的次數一起統計了進去,因此在最後需要去掉前導0的計數!
(思考這題的時候我其實是想從低位開始統計的,這樣就可以不用考慮前導0 的問題,但是思考到一半還是換到了從高位開始,我想從低位開始找規律應該也是可行的,有空的時候我再從低位開始找規律的分析下)

(這個過程就像是小學時候的做奧數題,找規律一樣~ 奧數題的話只要能找到規律算出答案就行了,但是算法呢還需要考慮得算法複雜度等,雖然不知道我寫的算法算不算效率高的, 至少沒有去一個一個數字遍歷~)

6. 算法實現的關鍵技巧
在算法中要尤其注意每次循環中對最高位的計算,以及最後要減去前導0的個數。

7.實驗分析及結論

public static void main(String[] args) {
        int page = 0;
        Scanner input;
        Formatter output;

        //讀取頁碼數
        try {
            input = new Scanner(Paths.get("input.txt"));
            if(input.hasNext()){
                page = input.nextInt();
            }
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //統計數字
        int count[] = count(page);

        //寫入文件
        try {
            output = new Formatter("output.txt");
            for(int num : count)
                output.format("%d%n", num);
            output.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static int[] count(int page){
        //初始化計數數組
        int count[] = new int[10];
        for(int i = 0; i < 10; i++){
            count[i] = 0;
        }

        //得出頁碼數的位/長度
        String str = Integer.toString(page);
        int len = str.length();

        int pre = 0;//前面最高位前面的數字
        int now = 0;//當前的最高位
        int rem = 0;//當前最高位後面的數字

        for(int i = 0; i < len; i++){
            rem = page % (int)(Math.pow(10.0, len-i-1));
            now = page / (int)(Math.pow(10.0, len-i-1));
            //計算小於當前最高位的數字在這個位置出現的次數
            for(int j = 0; j < now; j++)
                count[j] += Math.pow(10.0, len-i-1) * (pre + 1);
            //計算當前最高位的數字在這個位置出現的次數
            count[now] += Math.pow(10.0, len-i-1) * pre + rem + 1;
            //計算大於當前最高位的數字在這個位置出現的次數
            for(int j = now + 1; j <= 9; j++)
                count[j] += Math.pow(10.0, len-i-1) * pre;

            //將當前最高位加到前面的數字中
            pre = pre * 10 + now;
            //去掉當前最高位得到的數,也就是前面的餘數
            page = rem;

        }

        //去掉對前導0的計數
        for(int i = 0; i < len; i++){
            count[0] -= Math.pow(10.0, len-i-1);
        }

        return count;
    }

以上就是我的第一題算法題~
希望自己可以堅持下去 :)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章