如何将N!分解为素数的乘积

最近在翻看《算法竞赛入门经典》这本书,上面提到一篇关于如何将N!分解为素数的乘积的文章。原题大意如下:

数一个数 2

下面我们看一下《算法竞赛入门经典》的源码。后面我给出自己的改进解法。

int is_prime(int n)
{
	int i;
	for(i = 2; i <= sqrt(n); i++ )
	{
		if((n % i) == 0)
			return 0;
	}
	return 1;
}

void ConstructPrimeArray()
{
	int i;
	for(i = 2; i < 100; i++)
	{
		if(is_prime(i))
		{
			prime[count++] = i;
		}
	}
}



void GetIndexArray1(int input)
{
	int i;
	
	int j = 0;
	int m;
	
	
	for(i = 1; i <= input; i++)
		for( j = 0; j < count; j++)
		{	m = i;
			while(m % prime[j] == 0)
			{
				IndexArray[j] ++;
				m = m / prime[j];
				if(j > maxindex) maxindex = j;
			}
		}
}
上述函数:is_prime,主要判断一个数是否为素数,ConstructPrimeArray主要是获得100以内的素数,并将将其保存在prime数组中,而最重要的函数则是我们的GetIndexArray其主要获得每一个素数的指数,保存在IndexArray数组中(input为我们输入的那个数,count为100以内的素数在prime中的最大下标)。其主要思想是:从1到input的每一项,我们求其和每一个prime模(m%prime)如果是0则表示其是该素数的倍数,对该素数的指数有贡献,(while语句主要是判断对该指数的贡献是几?)最终将的贡献值存储在IndexArray中。maxindex主要是为了确定最大的素数因子的位置。

对于该算法的修改我们主要集中在GetIndexArray中,先给出代码如下:

void GetIndexArray2(int input)
{
	memset(IndexArray, 0, sizeof(IndexArray));
	int i = 0;
	int m;
	int ret = 0;
	 maxindex = 0;
	
	for(i = 0; i < count; i++)
	{	m = input;
		if(m / prime[i] == 0)
			break;
		while(m)
		{
			ret += m / prime[i];
			m = m / prime[i];
		}
		
		IndexArray[i] = ret;
		ret = 0;
		maxindex = i;
	}
}
我们现在的代码只剩下两个循环,并且减少了很多循环不是吗?

再解释代码之前我先给出《编程之美》中的一道问题。

即求N!十进制结果中末尾0的个数。末尾有几个0则N! = 2^x *3^y*5^z*.........,中Z的值就为几。因为只有5和偶数相乘才会出现0,又偶数的个数肯定比5的个数多所以末尾0的个数与5的指数相等。那么z究竟是多少呢,一个方法就是《算法竞赛入门经典》中给出的,而令一个方法就是:z = [N/5] + [N/(5^2)] + [N/(5^3)] +.......直到[N/(5^n)]为0。那么究竟为什么这个公式成立呢??

首先 N! = 1*2*3*4*5*6*......N 。那么[N/5]就表示从1到N每隔一个5总共有几个数是5的倍数,如N = 26 则 26/5 = 5;表示5,10,15,20,25会贡献一个5,那么我们会想25会贡献2个5啊!的确所以[N/(5^2)] 等于1,将另一个5计算在内。在这个问题中我们只计算了5的指数,而原题中需要计算所有的指数,那么我们只需要遍历一遍所有素数即可。这个就是上述我的改进程序的思想。好了就讲到这吧!!!!



你可能感兴趣的:(算法)