2.4 1的数目

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, 所以请大家指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值