编程之美 2.8 找符合条件的正数

题目: 任意给出一个正整数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; 无解的情况不是很理解,估计有什么数学上的推导结论

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值