批量分解素因数(一)

3 篇文章 0 订阅
3 篇文章 0 订阅
这个程序的功能是采用高效的算法,将n以内的的所有自然数分解质因数,并存储起来,最后输出其分解式。

1.自然数的分解
   根据算术基本定理,一个大于1的自然数能够唯一分解成几个素数乘积的形式。
    如120=2*2*2*3*5。我们这里,将自然数的分解式写成n=(p1^e1) * (p2^e2) * ... * (pm^em)的形式,这里p1,p2,pm表示素数,p1^e1, p2^e2表示素数的方幂。
   如120=(2^3)*(3^1)*(5^1)
   
2. 非重复素因子的个数,若一个自然数表示为几个素数乘积的形式,则去掉重复因子后,其因子的个数叫做这个自然数的非重复素因子的个数。如120含有3个非重复素因子,他们分别是2,3,5。容易看出,不同的自然数,其非重复素因子个数可能不同的,如27仅有1个非重复素因子3,而210含有2,3,5,7四个非重复素因子。2^32以内的所有整数中,其非重复素因子个数最多不超过9个,这个因为前10个素数的乘积2*3*5*7*11*13*17*19*23*29=6,469,693,230>2^32。
  
3.自然数分解式的表示
   若 n=(p1^e1) * (p2^e2) * ... * (pm^em),我们说这个自然数的分解式包含m项,每项包含2个成员,素因子p和其指数e。自然数的分解式可顺序存储,也可采用链式存储,前者其分解式的所有项的地址是相邻的,各项顺序存放。后者其分解式的每项的地址是不相邻的,使用单链表来存储。链式存需要额外的空间来存储指针,不但内存占用率高,且运行效率也低,故此文中的程序采用顺序存储方式。

4.算法  
   4.1 首先,函数sift采用与筛法求素数类似的方法,求出n以内所有的素数,并统计出每个合数的非重复素因子的个数。
   4.2 接着,程序算出所有n以内的所有自然数分解式的项数的之和,分配内存来存储其分解式,并确定每个自然数的分解式的的地址。方法是:若c_arr[i]表示自然数i的素因子的个数,a_arr[i]表示自然数i的的分解式的存储地址,则自然数i+1的的分解式的地址a_arr[i+1]=a_arr[i]+c_arr[i]
   4.3 然后,函数buildfactorTable将n以内的所有自然数分解,并存储其分解式。
   4.4 最后,函数print_factor输出每个自然数的分解式。

5.复杂度分析
5.1 空间复杂度
   函数batchDecompose 在运行过程中动态分配了3个数组,buff,addrtable和factorTable,前两个数组大小分别为n+1和n+2。最后一个数组的大小取决于n以内每个数的分解式的项数的和。n以内平均每个数的非重复素因子的个数约为log(log(n)),故factorTable的大小约为n*log(log(n)),故总的空间复杂度为o(n*log(log(n)))
5.2 时间复杂度
   函数sift的时间复杂度度亦为o(n*log(log(n))),计算addrTable的时间复杂度为o(n),函数buildfactorTable的时间复杂度稍大于o(n*log(log(n))),故总的时间复杂度稍大于o(n*log(log(n)))

下面是C语言源代码


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef unsigned char BYTE;
typedef unsigned long DWORD;

typedef struct _pe_pair
{
	DWORD p;	//p是素数
	DWORD e;	//e是指数
}PE_PAIR;

//筛出n以内(包括n)的所有素数,并统计n以内所有自然数的非重复素因子的个数
//当函数完成后,若pBuff[i]==0, 表明i是素数,
//若pBuff[i]>0, 表明i是合数,且i的非重复素因子的个数是pBuff[i]

void sift(BYTE *pBuff,DWORD n)
{
	DWORD p,m;
	memset(pBuff,0,sizeof(BYTE)*(n+1));
	p=2;
	while (p<=n)
	{
		for (m=p*2;m<=n;m+=p)	//p素数,m是p的倍数,将p的倍数(不包括p)的非重复素因子的个数增加1
			pBuff[m]++;
		
		p++;	//调到下一个数
		while ( pBuff[p] && p<=n) //略过合数
			p++;
	}
}

// 函数 print_factor输出n以内所有自然数的分解式
// addrTable是地址表,addrTable[i]表示自然数i的因子表的地址
// addrTable[i+1]-addrTable[i]表示自然数i的分解式的项数,addrTable包含n+2个元素,前3个元素不使用
// factorTable[addrTable[i]] 为自然数i的分解式的第一项
void print_expression(DWORD *addrTable,PE_PAIR *factorTable,DWORD n)
{
	DWORD i,j,c;
	PE_PAIR *tab;
	
	for (i=2;i<=n;i++)
	{
		c=addrTable[i+1]-addrTable[i];
		tab=factorTable+addrTable[i];

		printf("%u=",i);
		for(j=0;j<c;j++)
		{
			if (j>0)
				printf("*");
			printf("(%u^%u)",tab[j].p,tab[j].e);
		}
		printf("\n");
	}
}


/*函数buildTable对n以内的自然数进行分解,并将每个自然数的分解式存储到factorTable
输入参数
  n,pBuff,addrTable
输出参数
 factorTable
*/

void buildfactorTable(DWORD n,BYTE *pBuff, DWORD *addrTable,PE_PAIR *factorTable)
{
	DWORD i,p,m,t;
	PE_PAIR item;
	//-----------------------------------------------------
	p=2;
	while (p<=n)
	{
		item.p=p; item.e=1;
		factorTable[addrTable[p]]=item;
		addrTable[p]++;

		for (m=p*2;m<=n;m+=p)	//m为p的倍数
		{
			//m是p的倍数,故p一定整除m,故素因子p的指数至少为1
			item.p=p; item.e=1;	
			
			// m可能含有多个素因子p,如120含有3个因子2,下面的代码求出素因子p的指数
			t=m/p;	
			while (t%p==0) {t/=p; item.e++; }
			
			factorTable[addrTable[m]]=item;
			addrTable[m]++;
		}
		
		p++;
		while ( pBuff[p]>0 && p<=n) 
			p++;	//略过合数,使得p总是素数
	}
	
	//当完成上面的代码,addrTable[i]的值实际上为自然数i+1的因子表的首地址
	//故需要恢复addrTable为原始值
	for (i=n;i>=2;i--)
		addrTable[i+1]=addrTable[i];
	addrTable[2]=0;
}

//函数batchDecompose批量分解n以内所有自然数的,并打印其分解式
void batchDecompose(DWORD n)
{
	BYTE *buff=NULL;		//用来得到n以内的所有素数,和所有合数的素因子的个数
	DWORD *addrTable=NULL;	//地址表,addrTable[i]表示自然数i的分解式在factorTable中的偏移地址
	PE_PAIR *factorTable=NULL;
	DWORD i,c,total;

	buff		=(BYTE *)malloc(sizeof(BYTE)*(n+1));
	addrTable	=(DWORD *)malloc(sizeof(DWORD)*(n+2));
	if (buff==NULL || addrTable==NULL)
	{ printf("Alloc memory failed\n");	goto free_memory;}

	sift(buff,n);

	addrTable[0]=addrTable[1]=addrTable[2]=0;
	for (total=0,i=2;i<=n;i++)
	{
		c= (buff[i]==0 ? 1: buff[i]);	//c为自然数i的素因子的个数
		total += c;					  
		addrTable[i+1]=addrTable[i]+c;	//计算自然数i+1的分解式在factorTable中的偏移地址
	}

	factorTable=(PE_PAIR *)malloc(sizeof(PE_PAIR)*total);
	if (factorTable==NULL)
	{ printf("Alloc memory failed\n");	goto free_memory; }

	buildfactorTable(n,buff,addrTable,factorTable);	//将n以内的自然数的分解式存储到factorTable

	print_expression(addrTable,factorTable,n);	//打印将n以内所有自然数的分解式

free_memory:
	if (buff!=NULL) {free(buff);buff=NULL;}
	if (addrTable!=NULL) {free(addrTable);addrTable=NULL;}
	if (factorTable!=NULL) {free(factorTable);factorTable=NULL;}
}

int main(int argc, char* argv[])
{
	batchDecompose(100);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值