编程之美:第二章 数字之魅 2.8找到符合条件的整数

/*
找到符合条件的整数:
任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0

首先想到从小到大枚举M的取值,然后计算N*M,最后判断他们的乘积是否只含有1和0.
何时终止循环?

输入;
99
输出:
1 122 334 455 667 789
111 111 111 111 111 111

分析:
做问题的转化。由于问题中要求N*M的十进制表示形式里面只有1和0,所有N*M和M相比有特征。去搜索他们的乘积N*M,减少搜索空间。
另外搜索N*M,不去搜索M就是因为,当M很大时,当M>2^32时,机器无法存储M,我们可以只存储N*M的十进制表示中1的位置,缩小表示N*m需要的空间

把问题转化为 = 求一个最小的正整数X,使得X的十进制表示形式里面只含有1和0,并且X被N整除。
X从小到大的取值有:1,10,11,100,101,110,111,1000,1001,1010,1011,1100等等。
我们的目标是寻找最小的X,使得X mod N = 0,只要记录mod N = i(o<=i<N)的最小X就行了。避免不必要的循环。

设N=3,X=1,再引入一个变量J,J = X % N。直接遍历X,计算中间结果如下所示:
Num 1 2  3  4    5    6    7
N   3 3  3  3    3    3    3
x   1 10 11 100  101  110  111
j   1 1  2  1    2    2    0

表中计算110 % 3 是多余的,因为1和10对3的余数相同,所以101和110对3的余数相同,那么只需要判断101是否能够整除3就可以了,而不用判断110是否能整除3
。并且,如果X的最低3位是110,那么可以通过将101替换110得到一个符合条件的更小正整数。因此对于mod N同余的数,只需要记录最小的一个。

假设已经遍历了X的十进制表示有K位时的所有情况,而且也搜索了X = 10^K的情况,设10^K % N = a。现在要搜索X有K+1的情况,即X = 10^K + Y(0<Y<10^k)。
搜索空间(Y的取值)将有2^k-1个数据,但是如果对这个空间进行分解,即把Y按照其对N的余数分类,我们的搜索空间将被分成N-1个子空间。对于每个子空间,
其实只需要判断其中最小的元素加上10^K是否能被N整除即可,而没有必要判断这个子空间里所有元素加上10^K能否被N整除。
这样搜索空间就从2^k-1维压缩到了N-1维。但是这种压缩有一个前提,在前面的计算中已经保留了余数信息,并把Y的搜索空间进行分解。
分解:对于X模N的各种可能结果,保留一个对应的已经出现的最小的X【建立一个长度为N的余数信息数组,这个数组的第i位保留已经出现的最小的模N为i的X】

如何维护这个余数信息数组?假设有了X的十进制表示有K位的余数信息。也有了X=10^K的余数信息。现在搜索X有K+1位的情况,X=10^K + Y(0<Y<10^K),由于
已经有了对Y的按除N得余数进行空间分解的情况,即Y<10^K的余数信息组,现在只需要将10^K%N的结果与余数信息数组里面的非空元素相加,再取模N。看看会不会
出现新的余数即可。

总结:
假设最终的结果X有K位,直接遍历X,需要循环2^K次,按照保留余数信息数组的放,最多需要(K-1)*N步

下面是算法的伪代码:BigInt[i]表示模N等于i的最小符合的整数,只需要记下1的位置即可。即BigInt的每个元素是一个变长数组,对于模N等于i的最小X,
BigInt将存储最小X的1的位置,目标是求BigInt[0]
*/

你可能感兴趣的:(编程之美)