题目: 任意给出一个正整数N,求一个最小的正整数M(M>1),使得N * M 的十进制表示形式里只含有1和0.
method 1:
如书中给出的,最最直接的方法就是,从M =2,开始一次的加1,测试乘积是否满足条件。
伪代码:
for (int M = 2; ; M++)
{
product = N * M:
if (hasOnlyOneAndZero(product))
{
cout << product << endl;
}
}
此算法效率比较低。需要执行M次。
换一个方式思考,M满足条件时,乘积product也是第一次满足条件,即 product只含有0,1,并且product % N= 0;
由于product的遍历次数要比M的遍历次数少,所以我们可以从product入手。
method 2:
乘积product的可能取值为 1, 10, 11, 100, 101, 110, 111 ...
我们可以将其映射成二进制数 1, 2, 3, 4, 5, 6, 7 .....
然后遍历,当product%N == 0时,produc就是我们需要的最小乘积, procude/ N 就是我们需要的M值。
伪代码:
for (int i= 1; ; i++)
{
int decimal = DecimalFromMappedBinary(i);
if (decimal % N == 0)
{
cout << decimal;
}
}
插入我写的 二进制到十进制转换的函数:
// assume N * M = X;
// then X could be = 1, 10, 11, 100, 101, 110, 111 ...
// we can treat them as binary data, so it's mapping number is 1, 2, 3, 4, 5, 6, 7 ...
// the mapping rules are
// decimal:1 -> binary:1
// decimal:10 -> binary:2
// decimal:11 -> binary:3
// decimal:100 -> binary:4
// ...
// So all the decimal should only have 0,1 in each bit.
class BinaryDecimalMapping
{
public:
static int BinaryFromMappedDecimal(long long decimal)
{
}
static long long DecimalFromMappedBinary(int binary)
{
assert(binary >= 0);
int temp = 0;
for( int bit = 0; bit < sizeof(binary) * 8 ; bit++)
{
if (binary & (1 << bit))
{
temp |= (1 << (sizeof(binary) * 8 -1 - bit));
}
}
long long decimal = 0;
for (int i = 0; i < sizeof(binary) * 8; i++)
{
decimal = decimal * 10;
if (temp & 1)
{
decimal += 1;
}
temp >>= 1;
}
return decimal;
}
static bool isValidDecimal(long long decimal)
{
assert(decimal >= 0);
while(decimal)
{
if ((decimal % 10 != 0) && (decimal % 10) != 1)
{
return false;
}
decimal /= 10;
}
return true;
}
static int bitsCount(int binary)
{
int bits = 0;
while(binary)
{
bits++;
binary >>= 1;
}
return bits;
}
};
method 3:
method 2中,假设满足条件的乘积product的位数为K, 需要遍历2**K次。我们最后所要求的product需要满足product % N ==0, 即余数为0,
我们可以将product分成最高位 加上 其余位的和,如果最高位的余数remainder1 + 其余位remainder2 == N,那么这个product就是满足条件的值。
最高位我们可以一次求出,其余位一共有2**(K -2)个数,我们需要保存着这些值,为后面使用。
method 3 的时间复杂度级数与method2相同,由于使用加法代替除法效率应该要高一些。 空间复杂度为需要2**(K- 2)个额外的空间
method 4:
method 3, 我们在求出最高位的余数remainder1后,我们需要查找的是另一个余数N-remainder1,我们不需要遍历前面的2**(K-2)个数,只需要找出余数为N-remainder1的最小的数,它和最高位相加就是我们需要的成绩product。我们可以建立一个hash表来存储和查找,如果没有余数为N-remainder1的数,那我们需要用最高位时的所有数来更新hash表。
伪代码:(编程之美 p158)
for (int i =0; i < N; i++)
BigInt[i].clear();
BigInt[1].push_back(0);
int NoUpdate = 0;
for(int i = 0, int j = 10 % N; ; i++, j = (j * 10)% N)
{
bool flag = false;
if(BigInt[j].size() == 0)
{
flag = true;
BigInt[j].clear();
BigInt[j].push_back(i);
}
for (int k = 1; k < N; k++)
{
if (BigInt[k].size() > 0
&& (i > BigInt[k][BigInt[k].size() - 1])
&& (BigInt[(k + j) % N].size() = 0))
{
flag = true;
BigInt[(k + j) %N] = BigInt[k];
BigInt[(k + j) %N].push_back(i);
}
if (flag == false)
NoUpdate++;
else
NoUpdate = 0;
if (NoUpdata == N || BigInt[0].size() > 0)
break;
}
if (BigInt[0].size() == 0)
{
// M not exist
}
else
{
// find N * M = BigInt[0]
}
BigInt[i] 中存储的是余数为i的只含有1,0的最小数。记录的是1的位置,不是具体的数值。
对于代码中,NoUpdate == N,break; 无解的情况不是很理解,估计有什么数学上的推导结论