华为oj之质数因子

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mushao999/article/details/45540929

题目如下:

描述:  功能:输入一个正整数,按照从小到大的顺序输出它的所有质数的因子(如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[n];//标志数组
		//从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);
			}
		}		
	}
}

有思考不妥的地方还请个位看官指出,也可给我发邮件,[email protected]



 

猜你喜欢

转载自blog.csdn.net/mushao999/article/details/45540929