數學之美——用割補法實現數字a出現個數的問題

題目:給定一個整數n,計算所有小於等於n的非負整數中數字a(0<=a<=9)出現的個數
示例:輸入25178 ,5;輸出9717。
割補法示例回顧:在平面中給定一個不規則的圖形,需求其面積。
割:在圖形內部通過連對角線或者作垂線等手段將其分割成若干個規則圖形的面積來分別進行求積運算。
補:通過補充,將原不規則圖形放在一個規則的矩形或其它規則圖形的內部,然後減去其外部補充圖形的面積。

解題思路:在這裏面默認末尾都是0爲規則數字(因爲此時可以用排列組合原理直接算出某個數字出現的次數)運用割補法的思想,以25178,5爲例。
割:首先把0到25178分爲0到19999和20000到25178兩部分(由於2不等於5,這部分可以等價於0到5178),然後以此類推循環“降次”求每個位數中數字2出現次數。
補:依需求而補,千位數爲5等於要找的數字,在算千位中數字5出現的次數時可以先將其補充爲26000進行計算(此時,26000也可以等價於6000),然後在減去其多補充的數字5出現的次數。

割法的代碼如下:

public int countDigitOne1(int n,int b) {
            if (n < b)
                return 0;
            int len = getLenOfNum(n);
            if (len == 1)
                return 1;
            int tmp = (int) Math.pow(10, len - 1);
            int first = n / tmp; // 獲取n的最高位數字
            int firstOneNum = first ;
            if (firstOneNum > b) {
                firstOneNum = tmp;
            }
            else if (firstOneNum < b) {
                firstOneNum = 0;
            }
            else {
                firstOneNum = (n % tmp) + 1;
            }//獲取最高位數出現數字b的次數
            int otherOneNUm = first * (len - 1) * (tmp / 10);//固定最高位數字,其它位數字中數字b出現的次數
            return firstOneNum + otherOneNUm + countDigitOne1(n % tmp,b);//通過遞歸實現每次都把最高位降1,然後進行累加
        }

補法的代碼如下:

 public int countDigitOne2(int n,int b) {
           if (n < b)
               return 0;
           int len = getLenOfNum(n);
           if (len == 1)
               return 1;
           int tmp = (int) Math.pow(10, len - 1);
           int sum;
           if ((n / tmp) > b) {
               sum = tmp;
           }
           else if ((n / tmp) < b) {
               sum = 0;
           }
           else {
               sum = n % tmp + 1;
           }//獲取最高位數出現數字b的次數
           for (int i = len - 1; i > 0; i--) {
               int x = n / (int) Math.pow(10, i);// 獲取最高位數字
               int z = n / (int) Math.pow(10, i - 1);
               int y = z % 10;//獲取最高位的下一位數字
               if (y > b) {
                   sum = sum + (x + 1)*((int) Math.pow(10, i - 1));
               }
               else if (y < b) {
                   sum = sum + x * ((int) Math.pow(10, i - 1));
               }
               else {
                   sum = sum + (x + 1)*((int) Math.pow(10, i - 1)) - ((z + 1)*((int) Math.pow(10, i - 1)) - n - 1);
               }
           }
           return sum;
       }

在這裏還需要一個獲得數字n的位數的算法

private int getLenOfNum(int n) {
            int len = 0;
            while (n != 0) {
                len++;
                n /= 10;
            }
            return len;
        }//得到數字n的位數

以上就是用割補法的思想解決這個題目的全部解答,如有描述不當之處,還望讀者能夠指出。數學思想博大精深,此僅爲冰山一角,數學思想與計算機的融合更爲奇妙,願讀者與筆者一同學習,共同進步。

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