问题描述:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1, 2,…,9。
编程任务: 给定表示书的总页码的10 进制整数n (1≤n) 。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。
分析与解答:
由0,1,2……9组成的所有的n位数,从n个0到n个9共有10^n个n位数,其中全排列的情况下,每个数字使用的次数一样,设为f(n).
0 0 0 0 ...... 0
0 0 0 0 ...... 1
.............
9 9 9 9 ...... 9
公式可以这么理解:n位数字 数i的总次数为f(n) =
n-1位数字的数i的总次数f(n-1)与第一位为0-9 组合 = 10f(n-1)
+
第一位数字为数i,其他n-1位为0-9组合全排列的数i的次数 =10n-1
我们其实可以知道:f(n)=n10n-1
据此,可以从高向低位进行统计,再减去多余的0的个数即可。
void Statistical(int n) { //声明一个数组记录从0到9出现的次数 int[] count = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int len = (int)Math.Log10(n); //计算去掉最高位的位数 int tempLen = len; //位数为len的全排列的情况 每个数字出现次数 int mode = (int)Math.Pow(10, tempLen); int h = n / mode; //获取最高位数值 n %= mode; while (tempLen > 0) //直到当位数为2位以上 {
//计算去掉最高位数字段,每个数字使用的次数一样为f(n) n为去掉最高位后位数 根据全排列的情况下 for (int i = 0; i < 10; i++) { count[i] += h * tempLen * mode / 10; } //计算最高位为0~h-1时, 0~h-1 数字出现的次数为去掉最高位 全排列次数 for (int i = 0; i < h; i++) count[i] += mode; //计算最高位位h时出现次数为:去掉最高位的数字 + 1(加1是因为从0开始) count[h] += n + 1; mode = (int)Math.Pow(10, --tempLen); h = n / mode; n %= mode; } //计算当位数为1时的次数 for (int i = 0; i <= h; i++) count[i] += 1; //去掉前导0的个数 tempLen = len; for (int i = 0; i < len; i++) { mode = (int)Math.Pow(10, tempLen--); count[0] -= mode; } }