您的位置:首页 > 其它

华为oj之质数因子

2015-05-06 20:41 316 查看
题目如下:

描述: 功能:输入一个正整数,按照从小到大的顺序输出它的所有质数的因子(如180的质数因子为2 2 3 3 5 )

知识点: 排序

题目来源: 内部整理

练习阶段: 初级

运行时间限制: 10Sec

内存限制: 128MByte

输入:

输入一个long型整数

输出:

按照从小到大的顺序输出它的所有质数的因子,以空格隔开

样例输入: 180

样例输出: 2 2 3 3 5

思路:

传统的思维是从2到n遍历一遍(稍微优化一下可以到根号n),然后对每一个能被n整除的数判断是否为质数。这种方法的时间复杂度为O(n^2)。此处我提出的方法思路如下,假设一个数n的所有质数因子按照从小到大的顺序排列依次为a1,a2......ai即a1*a2...*ai=n,我采用不断放大步距的方法来求解。

初始的时候步距为1,从2开始每次增加1,找到第一能被n整除的数,那么这个数一定是a1,把a1加入结果,同时把步距放大为a1,即依次看a1*2,a1*3....a1*k,找到第一个能被n整除的a1*k,那么此时k一定为a2,把a2加入结果,然后把步距放大为a1*a2然后依次看a1*a2*2 a1*a2*3....重复上述过程直到乘积等于n,算法结束。

此方法的一个好处是不用判断是否为质数,得到的每个ai一定为质数(假设不为质数,那么从步距的2倍开始遍历还没有到它就已经找到能被n整除的数并放大步距了)。

该算法的时间复杂度也是一件有意思的事情,在找a1的是后进行了a1次操作,在找a2是进行了a2次操作,。。。所以时间复杂度为O(a1*a2.....*ai),而a1*a2......*ai正好为n,所以该算法的时间复杂度为O(n).

上代码:

import java.util.Scanner;
public class Main{
public static void main(String[] args)
{
Scanner sc=new  Scanner(System.in);
int num=sc.nextInt();
sc.close();
int tmp=1;
boolean isfirst=true;
while(tmp<=num)
{
int i=2;
while(tmp*i<=num&&num%(tmp*i)!=0)
{
i++;
}
tmp=tmp*i;
if(tmp<=num)
{
if(!isfirst)
{
System.out.print(" ");
}
else {
isfirst=false;
}
System.out.print(i);
}
}
}
}
说到这里想到了前一段时间和师兄讨论的一个求n以内的所有质数的精简方法,思路是这样的,如果一个数是质数,那么它的倍数都不是质数,因此采用一个大小为n的int数组作为标志,标志位的初始状态全置为0。算法从2开始遇到一个数没有被标记的数那么它一定是质数(否则一定被前面的一个数作为它的倍数标记了),把它的所有小于n的倍数对应的标志位全部置为1.这样一直往后走,遇到标志位为1的跳过,为0的就把它的倍数标记。

算法到根号n结束,因为我们的目的是标记所有的非质数,剩下的未被标记的就是质数.如果一个x小于n且不是质数,那么它可以分解为x=y*z,而y和z中至少有一个比根号n小(要不然x一定大于n),假设y是那个比根号n小的数,那么如果y为质数,那么y*z一定在遍历y时被标记了,如果y不是质数,那么它一定会被它的质数因子标记,而y*z也会在同一过程中被标记,所以大于根号n的非质数一定会在遍历到根号n之前被标记,所以遍历到根号n即可结束算法。

算法结束后,小于n的所有质数即为标志数组中所有为0的项目。(注意1不是质数,要从2开始看),遍历一下这个标志数组就可以得到所有质数。

另外要注意的是在标记一个质数的倍数时,对于质数i,要从它的i倍开始,因为它的小于i的倍数在前面遍历的时候被标记比如i*2肯定在遍历2的时候被标记了。

关于时间复杂度,由于对于所有的非质数标记且仅标记一次,所以时间复杂度为O(n).而对于一般的寻找质数的方法,时间复杂度为O(n^2),所以该算法在性能上有很大优势。

上代码:

import java.util.Scanner;
public class Main{
public static void main(String[] args)
{
Scanner sc=new  Scanner(System.in);
int n=sc.nextInt();
sc.close();
int[] flag=new int
;//标志数组
//从2开始遍历到根号n
for(int i=2;i*i<n;i++)
{
//如果未被标记则为质数,从i倍开始标记它的所有倍数
if(flag[i]==0)
{
for(int j=i;i*j<n;j++)
{
flag[i*j]=1;
}
}
}

//从2开始遍历输出结果
for(int i=2;i<n;i++)
{
if(flag[i]==0)
{
System.out.println(i);
}
}
}
}


有思考不妥的地方还请个位看官指出,也可给我发邮件,mushaoqiong@126.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: