所谓质因子分解是指将一个正整数n写成一个或多个指数的乘积的形式,例如6=2*3,8=2*2*2,180=2*2*2*3*3*5。或者也可以写成指数的形式,例如6=2^1*3^1,8=2^3,180=2^2*3^2*5^1。由此可以发现,质因子都是素数,不防先打印出其素数表,再进行记录。
由于每一个质因子都可以不止出现一次,因此不防定义结构体factor,用来存放质因子以及个数。如下:
struct factor {
int x,cnt; //x为质因子,xnt为其个数
}fac[10];
考虑到前10个素数相乘的结果已经超过了int的范围。因此对一个int型范围的数来说,fac数组的大小只需要开到10就可以了。
对一个正整数n来说,如果它存在1和本身之外的因子,那么一定是在sqrt(n)的左右成对出现。而这里把这个结论用在“质因子”上面,会得到一个强化结论:对一个正整数n来说,如果它存在[2,n]范围内的质因子,要么这些质因子全部小于等于sqrt(n),要么只存在一个大于sqrt(n)的质因子,而其余质因子全部小于等于sqrt(n)。这就给进行质因子分解提供了一个很好的思路:
①枚举1~sqrt(n)范围内的所有质因子p,判断p是否是n的因子。
- 如果p是n的因子,那么给fac数组中增加质因子p,并初始化其个数为0。然后,只要p还是n的因子,就让n不断除以p,每次操作令p的个数加1,直到p不再是n的因子为止。
if(n%prime[i]==0){ //如果prime是n的因子
fac[num].x = prime[i]; //记录该因子
fac[num].cnt = 0;
while(n%prime[i]==0){ //计算出质因子prime[i]的个数
fac[num].cnt++;
n/=prime[i];
}
num++;//不同质因子数加 1
}
- 如果p不是n的因子,就直接跳过。
if(n==1)
break; //及时退出循环,节省时间
②如果在上面步骤结束后n仍然大于1,说明n有且仅有一个大于sart(n)的质因子(有可能是n本身),这时需要把这个质因子加入fac数组,并令其个数为1。
if(n!=1) { //如果无法被根号n以内的质因子除尽
fac[num].x = n; //肯定有且仅有一个大于根号n的质因子
fac[num].cnt = 1; //置其个数为 1
}
质因子分解
程序代码:
//定义一个结构体,存放质因子以及其个数
const int maxn = 100010; //表长
int prime[maxn],pNum=0; //prime数组存放所有素数,pNum为素数个数
bool p[maxn] = {0}; //如果i为素数,则p[i]为false;否则p[i]为true
int num=0; //存储不同质因子的个数
struct factor{
int x,cnt; //x为质因子,cnt为其个数
}fac[10];
//筛选法打印素数表
void Find_Prime(int n) {
for(int i=2;i<n;i++){
if(p[i]==false){ //如果i是素数
prime[pNum++]=i; //把素数i存到prime数组中
for(int j=i;j<n;j+=i) { //筛去所有i的倍数,循环条j不能等于maxn
p[j]=true;
}
}
}
}
//存储质因子
void prime_factor(int n){
for(int i=0;i<pNum;i++){
if(n%prime[i]==0){ //如果prime是n的因子
fac[num].x = prime[i]; //记录该因子
fac[num].cnt = 0;
while(n%prime[i]==0){ //计算出质因子prime[i]的个数
fac[num].cnt++;
n/=prime[i];
}
num++;//不同质因子数加 1
}
if(n==1)
break; //及时退出循环,节省时间
}
if(n!=1) { //如果无法被根号n以内的质因子除尽
fac[num].x = n; //肯定有且仅有一个大于根号n的质因子
fac[num].cnt = 1; //置其个数为 1
}
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
Find_Prime(n);
prime_factor(n);
printf("%d=",n);
for(int i=0;i<num-1;i++) {
if(fac[i].cnt>0){
printf("%d^%d*",fac[i].x,fac[i].cnt);
}
}
printf("%d^%d\n",fac[num-1].x,fac[num-1].cnt);
//初始化数组
for(int i=0;i<10;i++){
fac[i].cnt=0;
fac[i].x=0;
}
}
return 0;
}
运行结果:
注意:
在以上的代码中,只要输入的数比较大就会出现程序异常,出现下图所示:
在以上的代码中出现这个原因在于打印素数表时,我们以输入的n为参数,如果n过大,运行超时导致异常:
//筛选法打印素数表
void Find_Prime(int n) {
for(int i=2;i<n;i++){
if(p[i]==false){ //如果i是素数
prime[pNum++]=i; //把素数i存到prime数组中
for(int j=i;j<n;j+=i) { //筛去所有i的倍数,循环条j不能等于maxn
p[j]=true;
}
}
}
}
正确的方法不应该以输入的n为界限来打印素数表,而是自己规定的一个值,此处我们规定maxn = 100010,作为打印素数表的上界,如下:
//筛选法打印素数表
void Find_Prime() {
for(int i=2;i<maxn;i++){
if(p[i]==false){ //如果i是素数
prime[pNum++]=i; //把素数i存到prime数组中
for(int j=i;j<maxn;j+=i) { //筛去所有i的倍数,循环条j不能等于maxn
p[j]=true;
}
}
}
}
以下是正确的程序代码(就是将打印素数表的判断上界改为maxn):
程序代码:
//定义一个结构体,存放质因子以及其个数
const int maxn = 100010; //表长
int prime[maxn],pNum=0; //prime数组存放所有素数,pNum为素数个数
bool p[maxn] = {0}; //如果i为素数,则p[i]为false;否则p[i]为true
int num=0; //存储不同质因子的个数
struct factor{
int x,cnt; //x为质因子,cnt为其个数
}fac[10];
//筛选法打印素数表
void Find_Prime() {
for(int i=2;i<maxn;i++){
if(p[i]==false){ //如果i是素数
prime[pNum++]=i; //把素数i存到prime数组中
for(int j=i;j<maxn;j+=i) { //筛去所有i的倍数,循环条j不能等于maxn
p[j]=true;
}
}
}
}
//存储质因子
void prime_factor(int n){
for(int i=0;i<pNum;i++){
if(n%prime[i]==0){ //如果prime是n的因子
fac[num].x = prime[i]; //记录该因子
fac[num].cnt = 0;
while(n%prime[i]==0){ //计算出质因子prime[i]的个数
fac[num].cnt++;
n/=prime[i];
}
num++;//不同质因子数加 1
}
if(n==1)
break; //及时退出循环,节省时间
}
if(n!=1) { //如果无法被根号n以内的质因子除尽
fac[num].x = n; //肯定有且仅有一个大于根号n的质因子
fac[num].cnt = 1; //置其个数为 1
}
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
Find_Prime();
prime_factor(n);
printf("%d=",n);
for(int i=0;i<num-1;i++) {
if(fac[i].cnt>0){
printf("%d^%d*",fac[i].x,fac[i].cnt);
}
}
printf("%d^%d\n",fac[num-1].x,fac[num-1].cnt);
//初始化数组
for(int i=0;i<10;i++){
fac[i].cnt=0;
fac[i].x=0;
}
}
return 0;
}
运行结果:
补充:
不使用素数表而直接打印输出的方法:
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=2;i<=sqrt(n);i++){
while(n%i==0){
printf("%d ",i);
n/=i;
}
}
if(n!=1)
printf("%d",n);
printf("\n");
}
return 0;
}
素数使用练习题:
Prime Factors