数学之美——用割补法实现数字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的位数

以上就是用割补法的思想解决这个题目的全部解答,如有描述不当之处,还望读者能够指出。数学思想博大精深,此仅为冰山一角,数学思想与计算机的融合更为奇妙,愿读者与笔者一同学习,共同进步。

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