【数论】因式分解 factor

因式分解

factor.pas/c/cpp

1S/256MB

【题目描述】

请写一个程序,读入一个正整数,把它的所有质因子找出来

【输入】

       输入文件:factor.in

输入一个正整数n,表示你要分解的数。

【输出】

      输出文件:factor.out

输出一行,把质因子按从小到大的顺序输出。

【输入样例】

60

【输出样例】

       2 3 5

【数据范围】

对于30%的数据,2<=n<=100000。

对于100%的数据,2<=n<=1000000000。





这一题本来很简单的数论知识的,但是我把他弄复杂了


先说说我的解法(后面再说正确的),可能有点麻烦,但是挺好理解的

对于一个数n,那么它必定至多有一个质因数是大于sqrt(n)的,所以我们就可以把问题分成两类来解决:找到小于sqrt(n)的和找到一个大于sqrt(n)的

然而sqrt(n)最大也才3万多,所以我们生成一个5万的素数表的话就可以轻易找出小于sqrt(n)的素数,那么如何找大于sqrt(n)的呢?

注意到,如果有一个数 x 是大于sqrt(n)的,并且是 n 的质因数,那么 n/x 就必定小于sqrt(n)!

所以方法就出来的,我们从2枚举到sqrt(n),然后每次判断当前是否为 n 的质因数,并判断 n/i 是否为质数(n/i 显然是n的因数了),并且只找一个大于sqrt(n)的质因数!

那么如何大数是否为质数呢?可以用到费马小定理,总之就是对于一个数m是质数,那么a^(m-1)%m=1,所以我们如果逆用这一定理,如果 a 取 2 , 7 , 61 都成立的话,那么当前的 m 就是质数(注意,如果m=2 , 7 , 61 的话就直接是质数了,不能用上面的定理来判断)

最后在判断一下 n 即可(因为如果n=3的话sqrt(n)以前是没有素数的)

本来是可以AC的,但是用费马小定理的话,很自然会用到快速幂,但是快速幂中写出bug了,两个 int 相乘时忘转long long,结果就乘爆了!

给出我的代码(代码之后是标准解法),C++ Code

/*
C++ Code
http://blog.csdn.net/jiangzh7
By Jiangzh
*/
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxp=50000;

int n,m;
bool h[maxp+10];
int pri[maxp+10];
int l=0;
int a[maxp+10];

void read()
{
	freopen("factor.in","r",stdin);
	freopen("factor.out","w",stdout);
	scanf("%d",&n);m=(int)sqrt((double)n);
}

void make_prime_list()// make prime list in [0,50000]
{
	for(int i=2;i<=maxp;i++)
	{
		if(!h[i]) pri[l++]=i;
		for(int j=0;j<l && pri[j]<=maxp/i;j++)
		{
			h[i*pri[j]]=true;
			if(i%pri[j]==0) break;
		}
	}
}

int mul(int x,int y,int mod)//x^y  % mod
{
	if(y==1) return x;
	if(y&1) return (mul((x*(long long)x)%mod,y/2,mod)*(long long)x)%mod;
	else return mul((x*(long long)x)%mod,y/2,mod)%mod;
}

bool is_prime(int x)
{
	if(x<=1) return false;
	if(x<=maxp) return !h[x];
	int t=mul(2,x-1,x);
	if(t!=1) return false;
	t=mul(7,x-1,x);
	if(t!=1) return false;
	t=mul(61,x-1,x);
	if(t!=1) return false;
	return true;
}

void work()
{
	make_prime_list();
	int len=0;
	bool flag=true;
	for(int i=2;i<=m;i++)
	if(n%i==0)
	{
		if(flag)
		{
			int y=n/i;
			if(y!=i && is_prime(y))
			{
				flag=false;
				a[len++]=y;
			}
		}
		if(!h[i]) a[len++]=i;
	}
	if(is_prime(n)) a[len++]=n;
	sort(a,a+len);
	for(int i=0;i<len-1;i++) printf("%d ",a[i]);
	printf("%d\n",a[len-1]);
}

int main()
{
	read();
	work();
	return 0;
}





下面再来看看标准解法

我们根据唯一分解定理(好像也叫代数定理),一个数 n 必定可以分解成几个质数的乘积,打个比方

300=2*2*3*5*5=2^2 * 3^1 * 5^2

那么我们可以根据这一定理,每次在素数表内给他进行筛选,每找到一个因数,就用n去不停地把这个因数除掉,比如300找到2时,就要把2除两个,变成75 。。。。。

每找到一个,输出即可

不过还有一个问题,比如35可以分解成5*7,但是sqrt(35)≈6

所以我们找出5后,n=7,然后sqrt(7)≈2.5,但是质数2是筛不掉的,所以7就会留下来,但是它也是一个质因数

所以说就存在这样一个问题,可能按照上面的方法分解完了之后的 n 可能不为1(为1的话就代表找完了所有的质数),如果n!=1的话,那么现在这个n就必定是原来n的一个质因数,并且是最大的(具体就不证明了,可以反证),所以输出到最后就可以了

我后来改了之后的代码

C++ Code

/*
C++ Code
http://blog.csdn.net/jiangzh7
By Jiangzh
*/
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxp=50000;

int n,m;
bool h[maxp+10];
int pri[maxp+10];
int l=0;

void read()
{
	freopen("factor.in","r",stdin);
	freopen("factor.out","w",stdout);
	scanf("%d",&n);
}

void make_prime_list()// make prime list in [0,50000]
{
	for(int i=2;i<=maxp;i++)
	{
		if(!h[i]) pri[l++]=i;
		for(int j=0;j<l && pri[j]<=maxp/i;j++)
		{
			h[i*pri[j]]=true;
			if(i%pri[j]==0) break;
		}
	}
}

void work()
{
	make_prime_list();
	for(int j=0;j<l && pri[j]*pri[j]<=n;j++)
	{
		if(n%pri[j]==0)
		{
			printf("%d ",pri[j]);
			while(n%pri[j]==0) n/=pri[j];
		}
	}
	if(n!=1) printf("%d",n);
}

int main()
{
	read();
	work();
	return 0;
}





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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值