2.8 寻找符合条件的数

1. 前言

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

2. 问题描述

这里写图片描述

3. 问题分析

这个问题主要 给定一个n, 然后寻找最小的能够整除n, 的并且只含有0, 1 的十进制数
解法一 : 穷举 0, 1, 10, 11, 100, 101, 110, 111, …
解法二 : 缓存<余数, 除以n余数为键的最小的整数>, 使用一个num计数, num从1开始, 每次迭代num*10, 遍历缓存表[遍历所有缓存余数], 如果num + entry.key能够被n整除, 那么则说明num + entry.key即为所求, 否则 判断num + entry.key 对应的余数不存在于缓存表, 则将<余数, num + entry.key> 添加到缓存表中, 所以缓存表中保存的就是 余数和余数对应的最小的能够被n整除的数字

这个思路减少了一些余数相同的数, 的重复计算 [比如 n为3, num为100, 假设之后的数为1, 10 [拼凑出101, 110], 因为1, 10 除以3 都余1 并且100除以3余数固定 , 所以这两个计算实际上是重复的, 而这里的思路, 就减少了这种重复的计算 ]

但是第二种思路的优势, 确实是挺难的, 吧备选的数字(们), 限制在了最多为n个
比如n为13, 而现在num跑到了1000000, 这样1000000, 之后的备选数字有2^6 64个, 而第二种思路, 将备选数字限制到了最多12个

编程之美分析原图 :
这里写图片描述

4. 代码

/**
 * file name : Test14ModNGotOnly01.java
 * created at : 9:21:48 AM May 23, 2015
 * created by 970655147
 */

package com.hx.test03;

import com.hx.util.Log;

public class Test15ModNGotOnly01 {

    // 获取能够整除n 并且只包含01的整数
    public static void main(String []args) {

        long start = System.currentTimeMillis();
        Random ran = new Random();
        int n = ran.nextInt(500);
//      int n = 111;

        Log.log(n);
        findModGotOnly0101(n);
        Log.enter();
        findModGotOnly0102(n);
        Log.horizon();

        long spent = System.currentTimeMillis() - start;
        Log.log("spent : " + spent + " ms...");

    }

    // 穷举 1, 10, 11, 100, ...
    public static void findModGotOnly0101(int n) {
        int i = 1;
        boolean isFound = false;

        while(!isFound) {
            int num = Integer.valueOf(Integer.toBinaryString(i ++ ));

            if((num % n) == 0 ) {
                isFound = true;
                Log.log(num);
                break;
            }

            if(num > (Integer.MAX_VALUE >> 1) ) {
                Log.log("specified number not find...");
                break;
            }
        }

    }

    // num : 1 -> 10 -> 100 -> ...
    // 先检查modValues中的对象
    // 在检查num能否被n整除
    // modValues存放于n计算<取得的模[除了0], 最小的值>
        // 比如 n为3   则1 -> 1, 2 -> 11
    // tmp用于存放本次计算中可以加入modValues中的entry[因为 如果直接在循环中加入modValues的话  会打乱计算]
        // 比如  如果num为100  如果计算得到了一个(1111 -> 3)可以加入modValues 
        // 如果直接加入的话  下一次循环计算的时候 会将这个1111也一并计算了 导致计算的不准确[1000 + 1111 = 2111]]
        // 所以先将可存储的entry 存入tmp中  循环结束  加入到modValues中
    public static void findModGotOnly0102(int n) {
        Map<Integer, Integer> modValues = new HashMap<Integer, Integer>(n - 1);
        Map<Integer, Integer> tmp = new HashMap<Integer, Integer>( (n >> 1) );

        int i = 1;
        int num = i;
        boolean isFound = false;
        while(!isFound) {
            int modVal = num % n;
            tmp.clear();

            for(Map.Entry<Integer, Integer> entry : modValues.entrySet() ) {
                int mayBeRes = entry.getKey() + modVal;
                int modVal02 = mayBeRes % n;
                if(modVal02 == 0) {
                    isFound = true;
                    Log.log(num + entry.getValue() );
                    break;
                } else {
                    if(!modValues.containsKey(modVal02) ) {
                        tmp.put(modVal02, num + entry.getValue());
                    }
                }
            }
            modValues.putAll(tmp);

            if(modVal == 0) {
                isFound = true;
                Log.log(num);
            } else {
                if(!modValues.containsKey(modVal) ) {
                    modValues.put(modVal, num);
                }
            }

            // 如果快要越界了还没有找到 则视为找不到了
            if(!isFound && (num > (Integer.MAX_VALUE >> 1)) ) {
                Log.log("specified number not find...");
                break;
            }

            num *= 10;
        }
    }

}

5. 运行结果

这里写图片描述

6. 总结

穷举的想法当然不是很难想到
第二种思路是非常巧妙的, 再再一次证明了数学与算法的紧密联系

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值