因式分解
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;
}