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