公因数与空间换时间思想

问题1.给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3628800,N!的末尾有两个0。

问题2.求N!的二进制表示中最低位1的位置。

问题3.丑数问题,把只包含因子2、3、5的数称作丑数。写一个函数,要求输入一个数值n(n>0),求从小到大的第n个丑数的值。

问题4.质数问题,要求写一个函数,输入数值n(n>0),求不小于n的质数的个数。(注意:问题3与问题4的问法其实在本质上是一样的。)

 

       首先分析问题1和2,这两个个题目就是关于阶乘的,而对于这样的问题用质因数分解法比较好处理。将N!进行因式分解:N!=2^X*3^Y*5^Z*T。

       问题1即是求N!的因式分解中Z的值,因为2*5=10,且X>=Z。

       问题2即是求N!的因式分解中X的值。

       首先给出一个求某数中因子t的指数的算法:

int indexOfT(int num,int t)
{
	int count = 0;
	int j;
	for(int i=1;i<=num;i++)
	{
		j=i;
		while(j%t==0)
		{
			count++;
			j/=t;
		}
	}
	return count;
}

    上面两题都可以循环调用这个函数来求结果。但是这样效率不高,对于N!求因式分解的指数,有个公式,例如求解Z:Z=[N/5]+[N/5^2]+[N/5^3]…。

    公式中[N/5]表示不大于N的数中5的倍数贡献一个5,,[N/5^2]表示不大于N的数中5^2的倍数再贡献一个5……代码如下:

count = 0;
while(n)
{
	n/=5;
	count +=n;
}

     现在来看丑数问题。

    我们可以循环求至第n个丑数来解决问题:

bool IsUgly(int number)
{
    while(number % 2 == 0)
        number /= 2;
    while(number % 3 == 0)
        number /= 3;
    while(number % 5 == 0)
        number /= 5;
 
    return (number == 1) ? true : false;
}

int GetUglyNumber_Solution1(int index)
{
    if(index <= 0)
        return 0;
 
    int number = 0;
    int uglyFound = 0;
    while(uglyFound < index)
    {
        ++number;
 
        if(IsUgly(number))
        {
            ++uglyFound;
        }
    }
 
    return number;
}

        这个代码效率低下,这里可以运用空间换时间的方式降低算法的时间复杂度。但是光想到空间换时间还不足够。其中还有点技巧,分析丑数的定义就可以知道,丑数应该是另一个丑数乘以2、3、5的结果(除1外),因此我们可以创建一个数组按顺序存储丑数。然后继续向后生成不在数组中的最小丑数,这个丑数肯定是之前丑数乘以2、3、5的结果,而这个数可以用t2、t3、t5进行标记(当然标记是动态的)。代码如下:

int min(int a,int b)
{
	return a<b?a:b;
}
int GetUglyNumber_Solution2(int index)
{
	if (index<=0)
		return 0;
	int* ugly=new int[index];
	int count=1;
	ugly[0]=1;
	int t2,t3,t5;
	t2=t3=t5=0;
	int a2,a3,a5,minNum;
	while (index>count)
	{
		count++;
		a2=2*ugly[t2];
		a3=3*ugly[t3];
		a5=5*ugly[t5];
		minNum=min(a2,min(a3,a5));
		ugly[count-1]=minNum;
		if (a2<=minNum)
			t2++;
		if (a3<=minNum)
			t3++;
		if (a5<=minNum)
			t5++;
	}
	int result=ugly[count-1];
	delete [] ugly;
	return result;
}

       最后来看质数问题。

       对于求n是否质数,仅需循环测试2<=i<=sqrt(n),是否能整除n。为了提高效率可以引入一个筛子,当然筛子的大小可以自定义,下面代码中定义的筛子大小为6:

bool isPrime(int number)
{
	if(number<=0) return false;
	int n=sqrt(number);
	for (int i=2;i<=n;i++)
		if (number%i==0) return false;
	return true;
}

int NumOfPrime(int index)
{
	if (index<0) 
		return -1;
	if (index==0) 
		return 0;
	int count=0;
	int i;
	if (index>6)
	{
		count = 4;
		int shaizi[6]={0,1,1,1,0,1};
		i=-1;
		for (int j=7;j<=index;j++)
		{
			i++;
			if (i==6) 
				i=0;
			if (shaizi[i]==0&&isPrime(j))
				count++;
		}
	}
	else
	{
		for (i=1;i<=index;i++)
			if (isPrime(i))
				count++;
	}
	return count;
}

       这个程序运用空间换时间的方式是:每次对于求取的结果保存下来,之后进行求取值的时候查找上次保存过的记录,这样可以迅速找到,并且如果求取的n比记录中最大的N大的化,也可以在N的基础上继续算其余的步骤。为了节省空间,可以保存一些特殊的点就可以了,然后程序会越跑越快。

       但是这个想法,与一般的思路不一样,不是算法的设计了,而是一种思想,甚至可以说有点作弊的味道。

       这里就不给出具体程序了。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值