不断优化的素数算法

前言:素数判断是算法中重要的一环,掌握优秀的素数判断方法是算法player的必修课。本文介绍的是由简到繁的素数算法,便于初学者从入门到精通。


素数(质数):只能被 1 和它本身整除的数称作素数,如:2、3、5、7、11等

一、🍓 暴力算法 🍓

  暴力算法是利用循环,看 2 − n 2 -n 2n 之间是否有能被 n n n 整除的数,若有,则 n n n 就是素数,否则就不是。
  Java代码如下: 时间复杂度为 O ( n ) O(n) O(n)

	/**
     * 不停地判断 2 ~ n-1 之间是否有数可以被 n 整除
     * @param n 输入的数字
     * @return 返回true为素数,false不为素数
     */
    public static boolean isPrime(int n){
        for (int i= 2; i < n; i ++){
            //只要有数能被 n 整除,就返回false
            if(n % i == 0)
                return false;
        }
        //没有数能被 n 整除就返回true
        return true;
    }

简单优化:
  其实循环的范围可以缩小到 n 的平方根,这是数学定理,就不过多赘述,这样时间复杂度就减小到了 O ( n ) O(\sqrt{n}) O(n ) ,那么代码就改变为如下:

	/**
     * 不停地判断 2 ~ 根号 n 之间是否有数可以被 n 整除
     * @param n 输入的数字
     * @return 返回true为素数,false不为素数
     */
    public static boolean isPrime(int n){
    	//Math.sqrt()的作用是求平方根
        for (int i= 2; i <= Math.sqrt(n); i ++){
            //只要有数能被 n 整除,就返回false
            if(n % i == 0)
                return false;
        }
        //没有数能被n整除就返回true
        return true;
    }

小结: 暴力算法只适合单个或少量的素数判断,若判断某个范围之间有哪些素数,时间复杂度可能会达到 O ( n n ) O(n \sqrt{n} ) O(nn ) ,如下代码就会达到。

	//求 n 以内的所有素数
	for (int i = 2; i <= n; i++){
        if(isPrime(i))
            System.out.println(i);
    }

另外还有简单优化的方法,如:跳过偶数(2除外)的判断等就不再讲述

二、🍉 埃氏筛法 🍉

  埃氏筛法是求 n 以内所有素数的方法,把不大于根号 n 的所有素数的倍数剔除,剩下的就是素数。方法及例子如下:

求 20 以内的所有素数:

  1. 列出 2 以后的所有序列:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  2. 标记数列的第一个数为素数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  3. 划掉被标记的数的所有倍数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    2 3 5 7 9 11 13 15 17 19
  4. 标记剩下数列中当前标记的下一个数为素数,再次重复执行以上操作,最后标记的数应为 n \sqrt{n} n (向下取整):
    2 3 5 7 9 11 13 15 17 19   标记 3
    2 3 5 7 9 11 13 15 17 19   删除 3 的倍数
    2 3 5 7 11 13 17 19      得到新的数列
    2 3 5 7 11 13 17 19      下一个数 5 大于了 n \sqrt{n} n ,就不用再标记了,当前数列已是结果
  5. 最后剩下的数都是 n 以内的素数:
    2 3 5 7 11 13 17 19

说明:在暴力算法那里我们提到,判断素数只需要判断到是否能被 n \sqrt{n} n 整除即可,所以在埃氏算法中,我们只需要标记到 n \sqrt{n} n ,就能把所有非素数剔除

图解:我们可以借助一个 int 型数组,当前单元的值为 1 表示当前下标的数是素数,否则不是素数,我们是借助 0 来完成上面第三步的划掉倍数的操作。上面的例子最终得到的数组如下:
在这里插入图片描述

该图就表示:2 3 5 7 11 13 17 19 都是素数,其他都不是。虽然 0 和 1 下标上的元素也是 1,但我们可以控制下标从 2 开始,毕竟 2 是第一个素数

Java代码实现:

	/**
     * 埃氏筛法
     * @param n 要求的是 n 以内的素数
     * @return 返回 int 型数组,0 表示当前下标数字不是素数,1 则就是素数
     */
    public static int[] sieve(int n){
        int[] arr = new int[n+1];// +1是为了让下标范围是 0 ~ n
        //让数组元素都为 1,即初始都为素数
        for (int i = 0; i <= n; i++)
            arr[i] = 1;
        //从 2 开始,若为素数就标记,并标记它的倍数不是素数
        for (int i = 2;i <= Math.sqrt(n); i++){//只需要标记到根号 n 即可
            if (arr[i] == 1){//素数才标记,因为当前位置可能是被标记的 0
                //j += i 控制 i 的倍数都被标记 0
                for (int j = 2 * i;j <= n;j += i){
                    arr[j] = 0;
                }
            }
        }
        return arr;
    }

小结:埃氏筛法的时间复杂度为 O ( n ∗ l o g n ) O(n*log n) O(nlogn),相对于暴力算法有了优化,适用于 10 6 以内范围的数据处理。常见的应用有:n 以内范围的素数求总和区间内的素数

三、🍈 欧拉筛法 🍈

  欧拉筛法是埃氏筛法的升级版。在埃氏筛法中,很多数都会被标记多次不是素数,例如 10 会在标记素数 2 、5 的时候都被标记不是素数,欧拉筛法则让每个数只被标记一次不是素数。使自身的时间复杂度达到线性的 O ( n ) O(n) O(n)。算法及例子如下,具体概念及证明就不在本文阐述,有兴趣的小伙伴可以去搜一下:

求 20 以内的所有素数:红色表示素数蓝色表示不是素数

  1. 列出 2 以后的所有序列,初始都标记为素数:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  2. 依次判断数列的每一个数,若为素数,则将其存放在一个数组中:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  3. 依次标记当前数的所有已找到素数倍的数不是素数:(当前所有已找到素数只有 2,当前数为 2,那么标记 2 * 2 = 4 不是素数)
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
  4. 标记一次,就判断当前数是否是当前素数的倍数,是则停止标记非素数,进入到下一个数的判断(这是使每个数只被标记一次的关键)
  5. 重复以上操作,直到判断到最后一个数(20):
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 3,是素数,先加入素数数组,当前素数有 2,3,则标记 6 和 9 不是素数
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 4,不是素数,不加入数组,当前素数有 2,3,则标记 8 不是素数,因为4是2的倍数,就结束标记(否则会重复标记12),对应第4步,进入下一个数
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 5,是素数,先加入数组,当前素数有 2,3,5,则标记 10 15 不是素数(25大于20,不用标记)
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  当前数为 6,不是素数,不加入数组,当前素数有 2,3,5,则标记 12 不是素数,因为 12 是 2 的倍数,就结束标记(可以看到前面阻止了对 12 的标记,避免和这里发生重复),进入下一个数
    后续操作与同理,最后会发现每个数只被标记了一次。最终的数如下:
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

说明:通过以上步骤可以得出,需要两个数组,一个存已找到的素数,一个标记各个数是否为素数的数组

Java代码实现:

	/**
     * 欧拉筛法求 n 以内的所有素数
     * @param isPrime 标记是否为素数的数组,1表示是素数,0则不是
     * @param prime 存放以求得的素数的数组
     * @param n 就是 n 本身
     * @return 返回一共有几个素数
     */
    public static int euler(int[] isPrime,int[] prime,int n){
        int cnt = 0;    //记录素数个数
        //让所有数组元素都为 1,即初始都为素数
        for (int i = 0;i <= n;i++)
            isPrime[i] = 1;
        //从 2 开始,若为素数就标记,并标记当前数的所有已找到素数倍的数不是素数
        for (int i = 2; i <= n; i++){
            //当前数为素数,则加入素数数组,并使 cnt计数加一
            if (isPrime[i] == 1) prime[++cnt] = i;
            //标记当前数的所有已找到素数倍的数不是素数
            for (int j = 1;j <= cnt && i * prime[j] <= n; j++){

                int k = i * prime[j];
                isPrime[k] = 0;
                //判断当前数是否是当前素数的倍数,是则停止标记非素数,进入到下一个数的判断
                //这里是使复杂度降低在线性的关键
                if (i % prime[j] == 0) break;
            }
        }
        return cnt;
    }

小结:欧拉筛将复杂度降低到线性,较于其他两种算法有了非常大的提升,但是远不及埃氏筛法容易理解,需要细细品味。但是算法嘛,很多时候都是先背板子,再刷题理解去掌握的,多用就好了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 遗传算法是一种模拟自然进化过程的优化算法,而BP神经网络是一种用于模式识别和函数逼近的人工神经网络算法。遗传算法优化BP的主要思想是通过利用遗传算法的优势来改进BP神经网络的收敛性和泛化能力。 首先,遗传算法可以用于改进BP算法的初始化权值和阈值。传统的BP神经网络需要手动初始化网络的权值和偏置,而这往往是一个经验性的工作,容易产生局部最优解。通过遗传算法,可以将权值和阈值作为基因编码,通过进化操作(如选择、交叉和变异)得到初始网络的权值和阈值,从而提高BP神经网络的初始状态。 其次,遗传算法可以用于改进BP算法的参数选择和调节。BP神经网络中的参数包括学习率、动量因子等,它们的选择对网络的学习效果有很大的影响。通过使用遗传算法,可以通过自动搜索和优化技术来确定这些参数的最佳取值,从而提高BP神经网络的性能。 最后,遗传算法还可以用于改进BP算法的隐藏层数量和神经元的个数。BP神经网络中的隐藏层数量和神经元个数的选择也是一个重要的问题,过大或过小都可能导致网络的拟合效果不佳。遗传算法可以通过编码隐藏层数量和神经元的个数,通过进化算子来搜索最佳的网络结构,从而提高BP神经网络的拟合能力。 综上所述,遗传算法可以通过改进BP神经网络的初始状态、参数选择和网络结构来优化BP算法,从而提高BP神经网络的学习能力和泛化能力。这种结合遗传算法和BP神经网络的优化方法,能够充分发挥两种算法的优势,提高神经网络的性能。 ### 回答2: C遗传算法优化BP(反向传播)是一种利用遗传算法优化BP算法的方法。BP算法是一种常用的神经网络训练算法,用于解决分类和回归等问题。但是,BP算法存在一些问题,如易陷入局部最优解、训练速度慢等。因此,使用遗传算法来改进BP算法是一种可行的方法。 遗传算法是一种基于自然进化规律的优化方法,主要包括选择、交叉和变异三个操作。在使用遗传算法优化BP算法时,首先需要确定染色体编码方式,例如可以用二进制表示BP网络的权值和阈值。然后,初始化一组个体(初始的权值和阈值),并经过一系列的选择、交叉和变异操作,不断迭代以生成新的个体。通过对适应度函数进行评估,筛选出适应度较高的个体,即具有较好的训练效果的BP网络参数。 通过不断地迭代和进化,遗传算法可以优化BP算法的权值和阈值,从而提高BP网络的性能。相比于传统的BP算法,利用遗传算法优化BP算法具有以下优势:首先,遗传算法可以并行处理,可加快训练速度;其次,遗传算法能够更好地避免陷入局部最优解;此外,遗传算法还可以在搜索空间中进行全局搜索,提高训练效果。 总而言之,C遗传算法优化BP是一种将遗传算法与BP算法结合的方法,通过遗传算法优化,可以改善传统BP算法的不足之处,提高神经网络的训练性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Easenyang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值