您的位置:首页 > 编程语言 > Java开发

2.4 1的数目

2015-08-12 21:25 861 查看

1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述



3. 问题分析

对于第一个问题 书中给出了两种算法

第一种 : 遍历一次1-n 统计出每一个数的出现的1的次数, 然后累加起来

第二种 : 利用数学方法找出规律, 然后计算 [呵呵, 一看就知道经过仔细思考过的算法效率高的多]

现在以 113为例解释一下第二种算法

1-113 中1出现的次数 = 1-113中[个位为1的数字的个数 + 十位为1的数字的个数 + 百位为1的数字的个数]

所以 1-113 中1出现的次数为 : 12 + 14 + 14 = 40

个位为1 : [[0*100 + [0-9] * 10] + 1] / [1*100 + [0-1]*10 + 1] = 11 + 1

十位为1 : [0 * 100 + 1*10 + [0-9]] / [1*100 + 1*10 + [0-3]] = 10 + 4

百位为1 : [1*100 + 0*10 + [0-9]] + [1*100 + 1*10 + [0-3]] = 14

通过归纳法 我们可以得到如下结论 :

因为 1 - n 中1的个数 等于个位为1的数的个数 + 百位为1的数的个数 + … + 最高位为1的数的个数

令mask 为当前测试位数的权重, [high表示当前位之前的数字的值, low表示当前位之后的数字的值, [比如 : 123, 设当前位为第二位2, 那么high为1, low为3]]

以第2位为例 :

如果第二位为0 则1-n中1的个数为 high * mask

如果第二位为1 则1-n中1的个数为 high * mask + low + 1

如果第二位为2-9 则1-n中1的个数为(high + 1) * mask

对于第二个问题

书中给出的解法为, 求解f(n) == n的上界, 然后在从上界开始递减 寻找满足条件的数字, 对于这个思路, 我看了一下 也是一知半解

证明存在上界 :





求出上界 :



add at 2016.02.12

今天在伯乐在线上面又看见了一种比较巧妙的解法, 这里 我就直接上图了

附上帖子的location : http://group.jobbole.com/13802/





4. 代码

/**
* file name : Test11FindNumOf1In1ToN.java
* created at : 10:02:01 AM May 20, 2015
* created by 970655147
*/

package com.hx.test03;

public class Test11FindNumberOf1In1ToN {

// 找到1到n 中所有的 1的个数[10进制表示]
public static void main(String []args) {

int n = 113;
//      1602000 1604000 1606000 1608000

//      findNumOf1In1ToN01(n);
findNumOf1In1ToN02(n);

//      for(int i=0; i<999999999; i+=1000) {
//          int num = findNumOf1In1ToN02(i);
//          Log.log(i, (i > num) );
//          if(i < num) {
//              Tools.awaitInput();
//          }
//      }

for(int i=0; i<100; i++) {
int num = findNumOf1In1ToN02(i);
//          Log.log(i, (i > num) );
Log.log(Integer.toBinaryString(num));
Tools.awaitInput();
}

}

// 从1遍历到n  统计共出现了多少个1
public static int findNumOf1In1ToN01(int n) {
int cnt = 0;
for(int i=1; i<=n; i++) {
cnt += numOf1InN(i);
}

Log.log(cnt);
return cnt;
}

// 统计n中出现了多少个1
private static int numOf1InN(int n) {
int cnt = 0;
while(n > 0) {
if(n % 10 == 1) {
cnt ++;
}
n /= 10;
}

return cnt;
}

// 思路 : 1 - n 中1的个数   等于个位为1的数的个数 + 百位为1的数的个数 + ... + 最高位为1的数的个数
// mask 为当前测试位数的权重
// 以第2位为例 : 如果第二位为0              则1-n中1的个数为  high * mask
// 如果第二位为1        则1-n中1的个数为  high * mask + low + 1
// 如果第二位为2-9  则1-n中1的个数为(high + 1) * mask
// 2103为例
// 第二位为0   十位为1的数的个数为高位[百位, 千位][0 - 20][21个] * 低位[个位][0 - 9][10个] = 21 * 10 = 210
// 2113为例
// 第二位为1  十位为1的数的个数为 (高位[百位, 千位][0 - 20][21个] * 低位[个位][0 - 9][10个]) + (高位为21[1个] * 低位为[0-3][4个]) = 22 * 10 + 3 + 1 = 224
// 2123为例
// 第二位为2   十位为1的数的个数为高位[百位, 千位][0 - 21][22个] * 低位[个位][0 - 9][10个] = 22 * 10 = 220
public static int findNumOf1In1ToN02(int n) {
int mask = 1, high = 0, low = 0, cur = 0;
int cnt = 0;

while(n >= mask) {
high = n / (mask * 10);
low = n % mask;
cur = (n / mask) % 10;

if(cur == 0) {
cnt += high * mask;
} else if(cur == 1) {
cnt += high * mask;
cnt += (low + 1);
} else {
cnt += (high + 1) * mask;
}

mask *= 10;
}

Log.log(cnt);
return cnt;
}

}


5. 运行结果



6. 总结

这里再一次的证明了, 算法和数学的紧密联系, …

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  编程之美 算法 java