java取素数(质数)

素数概念

  只能被1和本身整除,

需求

    指定一个值,取范围内的所有质数

1.普通算法

    基本概念:判断这个数,是否存在,能整除大于1小于本身的数(例:6可以整除,2和3,就不是质数)

    过滤条件:只判断最小的因数,(例如:6的因数,判断了2,就不需判断3)

    所以因数的取值范围最大就是,这个基数的开方。

	/**
	 * 方式1
	 */
	private void test1(int n) {
		int count=0;
		StringBuilder sb = new StringBuilder();
		sb.append(2);
		sb.append(",");
		for(int i = 3;i<=n;i++){
			boolean isP=true;
			for(int j = 2;j <= Math.sqrt(i);j++){
				count+=1;
				if(i%j==0) {
					isP=false;
					break;
				}
			}
			if(isP) {
				sb.append(i);
				sb.append(",");
			}

		}
		System.out.println(count+":"+sb.toString());
	}

2.埃式筛法(每找到一个素数将它的倍数筛掉)

    基本概念:当2是质数,将4,6,8...都筛掉,剩下的继续

        下一个处理3是质数,将3的倍数,6,9,12..都筛掉,剩下继续

        下一个5,以此类推。

    缺陷:每个相同的倍数被筛选多次(例如:6会被2和3都做一次筛选)

	/**
	 * 方式二
	 * 埃式筛法
	 */
	private void test2(int n) {
		// 从2开始找
		// 每找到一个将倍速筛掉
		
		// 素数集合
		int[] primes = new int[n+1];
		int cnt=0;// 素数集合下标
		// 初期化,下标默认判断
		boolean[] st = new boolean[n+1];
		for (int i = 0; i<=n; i++) {
			st[i] = false;
		}
		int count=0;
		for (int i = 2; i<=n; i++) {
			// 判定当前下标值是否已经判定过了,例如6在2判定过,3不再判定
			if (!st[i]) {
				primes[cnt++]=i;
				for (int j=i*i; j<=n; j+=i) {
					//这里2的场合筛选了4,6,8,10,12..没意义
					//3的场合,会筛12,重复了
					st[j] = true;
					count+=1;
				}
			}
		}
		StringBuilder sb = new StringBuilder();
		for (int i=0; i<primes.length;i++) {
			sb.append(primes[i]);
			sb.append(",");
		}
		System.out.println(count+":"+sb.toString());
	}

3.欧拉筛法

    基本概念: 筛掉当前处理的基数的比这个基数小的所有的质数的倍数(例如:基数2,将基数2和质数2的倍数4筛掉,处理基数3就将,3和2的倍数6筛掉,处理4就将,2*4和3*4筛掉,以此类推)

    原理1:双数一定会被2筛掉。

    原理2:为什么用质数的倍数,因为不是质数,就一定有比本身小的因数,在处理比本身小的因数时就已经筛过了。

	/**
	 * 方式三
	 * 欧拉筛法(埃式筛法的优化)
	 */
	private void test3(int n) {
		// 素数集合
		int[] primes = new int[n+1];
		int cnt=0;// 素数集合下标
		
		int count=0;
		
		// 初期化,下标默认判断
		boolean[] st = new boolean[n+1];
		for (int i = 0; i<=n; i++) {
			st[i] = false;
		}
		
		for (int i = 2; i<=n; i++) {
			if (!st[i]) {
				primes[cnt++]=i;
			}
			for (int j=0;j<cnt && i * primes[j] <= n;j++) {
				// 去掉质数的倍数
				st[primes[j]*i] = true;
				count+=1;
				// 除质数能除开,便不是质数
				if(i%primes[j]==0)
					break;
			}
		}
		StringBuilder sb = new StringBuilder();
		for (int i=0; i<primes.length;i++) {
			sb.append(primes[i]);
			sb.append(",");
		}
		System.out.println(count+":"+sb.toString());
	}

4.自定义函数(自己改了一个函数)

    原理1:2以外,双数不做判断(直接用i+=2来循环)节约一半的处理。

    原理2:只判断质数的倍数(参照上记3)

    原理3:倍数的选择(不是1开始累加)而是从自身的平方开始(例如7的平方,49开始筛查,如果是21在3的倍数筛过滤,所以7不筛了)

    原理4:只筛查双数倍(例如:7从49筛,那么单数倍56不筛查(因为一定是偶数),只筛查双数倍63开始)

private void test4(int n) {
		// 初期化,下标默认判断
		boolean[] st = new boolean[n+1];
		
		// 素数集合
		int[] primes = new int[n+1];
		int cnt=0;// 素数集合下标
		primes[cnt++]=2;
		int count=0;
		for (int i = 3; i<=n; i+=2) {
			if (!st[i]) {
				primes[cnt++]=i;
				for (int j=i*i; j<=n; j+=(i*2)) {
					st[j] = true;
					count+=1;
				}
			}
		}
		StringBuilder sb = new StringBuilder();
		for (int i=0; i<primes.length;i++) {
			sb.append(primes[i]);
			sb.append(",");
		}
		System.out.println(count+":"+sb.toString());
	}

总结:

注意:本文代码以Int为类型,所以不要传,比int最大值的开方大的数会异常,可以将代码中i*i<n的部分修正为i<sqrt(n))

测试:以取10000以内素数为例,

方法1:运算了 117,527次,

方法2:运行了  16,981次,

方法3:运行了    8,770次,

方法4:运行了    5,995次,

只限于循环次数,没有计算运行时间,具体效率大家自行摸索。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值