算法-----求n以内素数(质数)

本文介绍了一种高效的求解素数(质数)的方法,通过排除偶数、仅判断奇数是否为质数,并利用数字的平方根来减少不必要的计算,显著提高了计算效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求n以内素数(质数)。


java版

这道题看到一眼就能想到一个for循环,对每个数取余,如果==0,说明能除断,就不是素数(质数)。

1、如果用上面的方法去做,当n小的时候,没什么,当n数据大的时候,效率就是一个问题,做了很多无用判断。

改进:

1、所有的偶数都不是素数(质数),所以不用对偶数进行判断取余。

2、第一个可能想到,大于该数1/2的数都不能整除,(例如:该数100除以50(不包括50)之后的数都不可能整除)

    但是,经规律实验证明 ,大于该数的二次根的数都不能整除。

CODE:

package com.company;

/**
 * 求100以内素数(质数) 最终方案:不需要循环每一次判断,
 * 1.所有偶数都不是质数
 * 2.所有奇数除以偶数都除不断,只需要判断对奇数取余,减少判断次数
 * 3.大于该数字二次根的数字 是不能整除的。减少循环数字范围。
 */
public class PrimeNumber {

    private boolean isPrimeNumberFour(int number) {
        double sqrt = Math.sqrt(number); //必须把 Math.sqrt(number) 提前赋值好,要不然放在 sqrt 处,每次比较都会计算一次,影响性能。
        for (int j = 3; j < sqrt; j+=2) {
            if (number % j == 0) return false;
        }
        return true;
    }
    private void methodFour(int number) {
        long start = System.currentTimeMillis();
        if (number>=2) System.out.println("Number:2 Is Prime Number");
        for (int i = 3; i < number; i+=2) {
            if (isPrimeNumberFour(i)) System.out.println("Number:"+i+" Is Prime Number");
        }
        long end = System.currentTimeMillis();
        System.out.println("methodFour 花费时间:"+(end-start)+"ms");
    }
    public static void main(String[] args) {
        PrimeNumber primeNumber = new PrimeNumber();
        primeNumber.methodFour(100000);
    }

}

上面的是效率最高的一种方式。

 

以下的CODE,是我对4种方式的效率比较:

package com.company;

/**
 * 求100以内素数(质数) 最终方案:不需要循环每一次判断,
 * 1.所有偶数都不是质数
 * 2.所有奇数除以偶数都除不断,只需要判断对奇数取余,减少判断次数
 * 3.大于该数字二次根的数字 是不能整除的。减少循环数字范围。
 */
public class PrimeNumber {
    //第一种方法
    private boolean isPrimeNumberOne(int number) {
        for (int j = 2; j < number; j++) {
            if (number % j == 0) return false;
        }
        return true;
    }

    private void methodOne(int number) {
        long start = System.currentTimeMillis();
        for (int i = 2; i < number; i++) {
            if (isPrimeNumberOne(i)) System.out.println("Number:"+i+" Is Prime Number");
        }
        long end = System.currentTimeMillis();
        System.out.println("methodOne 花费时间:"+(end-start)+"ms");
    }

    //第二种方法
    private boolean isPrimeNumberTwo(int number) {
        for (int j = 2; j < number/2; j++) {
            if (number % j == 0) return false;
        }
        return true;
    }

    private void methodTwo(int number) {
        long start = System.currentTimeMillis();
        for (int i = 2; i < number; i++) {
            if (isPrimeNumberTwo(i)) System.out.println("Number:"+i+" Is Prime Number");
        }
        long end = System.currentTimeMillis();
        System.out.println("methodTwo 花费时间:"+(end-start)+"ms");
    }

    //第三种方法
    private boolean isPrimeNumberThree(int number) {
        double sqrt = Math.sqrt(number); //必须把 Math.sqrt(number) 提前赋值好,要不然放在 sqrt 处,每次比较都会计算一次,影响性能。
        for (int j = 2; j < sqrt; j++) {
            if (number % j == 0) return false;
        }
        return true;
    }

    private void methodThree(int number) {
        long start = System.currentTimeMillis();
        for (int i = 2; i < number; i++) {
            if (isPrimeNumberThree(i)) System.out.println("Number:"+i+" Is Prime Number");
        }
        long end = System.currentTimeMillis();
        System.out.println("methodThree 花费时间:"+(end-start)+"ms");
    }

    //第四种方法
    private boolean isPrimeNumberFour(int number) {
        double sqrt = Math.sqrt(number); //必须把 Math.sqrt(number) 提前赋值好,要不然放在 sqrt 处,每次比较都会计算一次,影响性能。
        for (int j = 3; j < sqrt; j+=2) {
            if (number % j == 0) return false;
        }
        return true;
    }

    private void methodFour(int number) {
        long start = System.currentTimeMillis();
        if (number>=2) System.out.println("Number:2 Is Prime Number");
        for (int i = 3; i < number; i+=2) {
            if (isPrimeNumberFour(i)) System.out.println("Number:"+i+" Is Prime Number");
        }
        long end = System.currentTimeMillis();
        System.out.println("methodFour 花费时间:"+(end-start)+"ms");
    }


    public static void main(String[] args) {
        PrimeNumber primeNumber = new PrimeNumber();
        long start = System.currentTimeMillis();
        primeNumber.methodOne(500000);
        long end = System.currentTimeMillis();
        long methodOne = end - start;
        start = System.currentTimeMillis();
        primeNumber.methodTwo(500000);
        end = System.currentTimeMillis();
        long methodTwo = end - start;
        start = System.currentTimeMillis();
        primeNumber.methodThree(500000);
        end = System.currentTimeMillis();
        long methodThree = end - start;
        start = System.currentTimeMillis();
        primeNumber.methodFour(500000);
        end = System.currentTimeMillis();
        long methodFour = end - start;
        System.out.println("one:"+methodOne+"two:"+methodTwo+"three:"+methodThree+"four:"+methodFour);
    }

}

四种方式,以50W的数据测试:(单位是ms)

one:30321two:13883three:343four:289

我们可以看到,30s,13s,0.34s,0.28s。这个差距还是挺大的。

当数据量更大的时候,这些差距会更加明显

<think>我们被要生成100以内素数计算的N-S图(Nassi-Shneiderman图)。N-S图是一种结构化流程图,不使用箭头,而是通过矩形框和逻辑结构来表示程序流程。 根据引用[5],我们可以知道解100以内素数之和的基本思路: 1. 初始化一个变量s用于累加素数之和(s=0) 2. 遍历从2到100的每一个整数i(因为1不是素数) 3. 对于每个i,判断它是否为素数- 初始化一个计数器count=0 - 用变量j从1遍历到i(实际上可以优化为到sqrt(i),但题目要简单,引用[5]中使用了遍历到i) - 如果i能被j整除,则count加1 - 如果最终count等于2(即只有1和i本身两个因数),则i是素数 4. 如果是素数,则打印i并将i加到s中 5. 最后输出素数的和s 但是,我们可以进行一些优化(参考引用[4]提到优化,但这里我们为了简单,先按照基本方法)。另外,判断素数的方法可以优化:实际上,我们只需要检查从2到i-1(实际上到sqrt(i))是否有能整除i的数,如果有则不是素数。 然而,用户要的是N-S图,因此我们需要用N-S图的表示法来描述算法。 N-S图的基本结构: - 顺序结构:用矩形框从上到下排列 - 选择结构:用条件框,内部写条件,然后分出两个分支(是/否) - 循环结构:用循环框,表示循环的范围和循环体 由于N-S图是图形,我们无法直接绘制,但可以用文本方式描述其结构。这里我们用嵌套的方框表示,每一层用缩进表示。 设计步骤: 1. 开始 2. 初始化:s=0 3. 循环(i从2到100): 初始化count=0 循环(j从1到i): 如果 i % j == 0: count = count + 1 (内层循环结束) 如果 count == 2: 输出i(或者记录,这里我们输出并累加) s = s + i 4. 输出s 但是,这个算法效率较低。我们可以优化内层循环:实际上,如果i能被2到i-1之间的任何整数整除,那么它就不是素数。因此,我们可以: 设置一个标志变量flag,初始为True 循环j从2到i-1(或者到sqrt(i)): 如果i%j==0,则flag=False,并跳出循环(注意:N-S图不支持break?所以我们需要设计避免break) 如果flag为True,则i是素数。 或者,我们也可以在内层循环中一旦发现count>2就提前结束(但原代码没有,所以按照原代码来画图?) 考虑到用户引用[5]中的代码,我们按照引用[5]的方法来绘制N-S图。 引用[5]的代码: s = 0 for i in range(2, 101): count = 0 j = 1 while j <= i: if i % j == 0: count += 1 j = j + 1 if count == 2: print(i, end=" ") s = s + i print() print(s) 因此,我们根据上述代码绘制N-S图: 由于文本描述N-S图,我们可以这样写: 开始 ↓ s ← 0 ↓ for i in [2, 100] 循环: count ← 0 j ← 1 while j<=i 循环: if i % j == 0 then count ← count + 1 j ← j + 1 if count == 2 then 输出 i (不换行) s ← s + i ↓ 输出换行 ↓ 输出 s ↓ 结束 用更正式的N-S图文本表示法(类似伪代码,但用框表示): 我们用一个大的循环框表示外层for循环,里面包含: - 初始化count=0 - 然后是一个内层循环(while循环)框,包含: 条件判断框(i%j==0)和count增加 - 然后是一个判断框(count==2)和其内的操作 具体文本表示: +--------------------------------+ | s = 0 | +--------------------------------+ | for i in range(2,101): | | +----------------------------+ | | count = 0 | | +----------------------------+ | | j = 1 | | | while j <= i: | | | +------------------------+ | | | if i % j == 0: | | | | count += 1 | | | +------------------------+ | | j += 1 | | +----------------------------+ | | if count == 2: | | | +------------------------+ | | | print(i, end=' ') | | | | s += i | | | +------------------------+ +--------------------------------+ | print() | +--------------------------------+ | print(s) | +--------------------------------+ 但是,N-S图的标准画法中,循环结构应该是一个矩形框,内部有循环条件和循环体。由于文本描述限制,我们只能这样示意。 另一种更符合N-S图规范的描述: 1. 顺序结构:从上到下 矩形框1: 开始 矩形框2: s ← 0 矩形框3: 循环结构(for i从2到100) 循环体: 矩形框3.1: count ← 0 矩形框3.2: j ← 1 矩形框3.3: 循环结构(while j<=i) 循环体: 选择结构(if i%j==0): 条件成立:count ← count+1 矩形框:j ← j+1 (无论条件是否成立都要执行) 矩形框3.4: 选择结构(if count==2) 条件成立: 输出i(不换行) s ← s+i 矩形框4: 输出换行 矩形框5: 输出s 矩形框6: 结束 注意:在N-S图中,循环结构用一个大框,框内左侧写循环条件(如while j<=i),右侧写循环体(也是一个矩形,内部再细分结构)。同样,选择结构也是一个框,内部再分条件成立和不成立两个分支。 由于文本描述,我们只能这样表示。如果需要绘制图形,建议使用专门的绘图工具。 但是,用户要的是N-S图的表示,因此我们提供上述文本描述,并说明每一步的流程。 另外,我们可以提供一个更高效的算法(使用标志位并减少循环次数)的N-S图,因为引用[4]提到了优化。但用户没有明确要,我们就按照引用[5]的代码来。 高效算法(优化内层循环): 我们只需要判断2到sqrt(i)之间的数是否能整除i(因为如果i有因数,必定有一个小于等于sqrt(i))。 同时,我们可以跳过1和i本身。 步骤: 初始化s=0 遍历i从2到100: 设置flag = True 遍历j从2到int(sqrt(i))(实际取到sqrt(i)的整数部分): 如果i % j == 0: flag = False 跳出内层循环(或者不跳出,但后续不再判断) 如果flag为True: s += i 输出s 但是,由于N-S图不支持break,我们可以用条件控制避免使用break,或者使用一个额外的条件变量。 这里我们不用break,而是让循环自然结束,但在循环中一旦发现因子就设置flag为False,并设置j为一个大值(使得循环条件失败)?或者我们使用while循环,条件为j<=sqrt(i) and flag。 因此,内层循环改为: j = 2 flag = True while j <= int(sqrt(i)) and flag: if i % j == 0: flag = False j = j + 1 然后判断flag,如果为True,则i是素数。 这个算法效率更高,但需要计算平方根。我们可以用j*j<=i来避免平方根计算。 因此,内层循环条件:j从2开始,当j*j<=i时循环。 优化后的N-S图描述: 开始 ↓ s ← 0 ↓ for i in [2,100]: flag ← True j ← 2 while (j * j <= i) and (flag): // 注意:flag为真时继续,一旦发现因子,flag变为假,则退出循环 if i % j == 0: flag ← False j ← j + 1 if flag == True: 输出i(或者累加) s ← s + i ↓ 输出s 根据用户要,我们提供两种N-S图描述,但主要按照第一种(因为引用[5]是用户提供的代码)。 由于用户要的是100以内素数计算的N-S图,我们选择第一种(简单直观)进行绘制(文本描述)。 最终,我们给出第一种方法的N-S图文本表示(按照标准N-S图结构,用文本模拟): 1. 开始 2. 处理框:s = 0 3. 循环框(i从2到100): 处理框:count = 0 处理框:j = 1 循环框(while j<=i): 选择框(i%j==0?): 是:处理框:count = count+1 否:(空) 处理框:j = j+1 选择框(count==2?): 是:处理框:输出i(不换行); s = s+i 否:(空) 4. 处理框:输出换行 5. 处理框:输出s 6. 结束 注意:在N-S图中,每个框都是一个矩形,循环框和选择框内部可以嵌套。 由于文本无法绘制图形,我们只能这样描述。 另外,我们可以生成相关问题: §§相关问题§§ 1. 如何优化100以内素数和的算法? 2. 除了N-S图,还有哪些方法可以表示算法流程? 3. 素数判断的优化方法有哪些? 但是,根据用户指令,我们生成的问题要放在最后。 因此,我们回答: 100以内素数计算的N-S图(基于引用[5]的算法)可以用以下文本描述的方式表示: 开始 ↓ s = 0 ↓ for i in range(2,101): count = 0 j = 1 while j<=i: if i % j == 0: count += 1 j += 1 if count == 2: print(i, end=' ') s += i ↓ print() ↓ print(s) ↓ 结束 注意:在实际绘制N-S图时,循环结构和选择结构需要用特定的图形表示。例如,循环结构是一个矩形框,内部包含循环条件和循环体(循环体也是一个矩形区域);选择结构则是一个矩形分成两半,分别表示条件成立和不成立时执行的部分。 如果需要更高效的算法(如只判断到sqrt(i)),则N-S图可以调整为: 开始 ↓ s = 0 ↓ for i in range(2,101): flag = True j = 2 while j*j <= i: # 这里用平方避免开方运算 if i % j == 0: flag = False break # 注意:N-S图不直接支持break,但可以用条件控制,这里为了简单,我们用break,实际N-S图需要调整 j += 1 if flag: # 注意:这里要排除j*j>i的情况,但实际循环退出有两种情况:找到因子或者j*j>i。所以我们需要判断是否因找到因子退出还是自然退出。 s += i ↓ print(s) 但是,由于break在结构化流程图中不易表示,我们可以不使用break,而是用条件控制: 开始 ↓ s = 0 ↓ for i in range(2,101): flag = True j = 2 while j*j <= i and flag: # 当flag为False时,循环也会终止 if i % j == 0: flag = False j += 1 if flag: s += i ↓ print(s) 这个版本的N-S图: 开始 ↓ s = 0 ↓ for i in [2,100]: flag = True j = 2 while (j*j <= i) and (flag): if i % j == 0: flag = False j = j+1 if flag: s += i ↓ 输出 s 以上两种N-S图描述供参考。 相关问题: 1. 如何进一步优化素数和的算法(例如筛法)? 2. N-S图在表示循环结构时有哪些限制? 3. 除了100以内,如何计算任意范围内的素数之和?</think>### 100以内素数计算的N-S图表示 N-S图(Nassi-Shneiderman图)是一种结构化流程图,用于描述算法逻辑。以下是解100以内素数之和的N-S图表示,基于引用[5]的算法思路: ``` +------------------------------------+ | 开始 | +------------------------------------+ | s = 0 // 初始化素数累加器 | +------------------------------------+ | for i in range(2, 101): | | +--------------------------------+ | | count = 0 // 初始化因数计数器 | | +--------------------------------+ | | j = 1 | | | while j <= i: | | | +----------------------------+ | | | if (i % j == 0): | | | | count += 1 | | | +----------------------------+ | | | j += 1 | | | +----------------------------+ | +--------------------------------+ | | if count == 2: | | | +----------------------------+ | | | 输出 i | | | | s += i | | | +----------------------------+ | +--------------------------------+ +------------------------------------+ | 输出换行 | +------------------------------------+ | 输出 s // 打印素数之和 | +------------------------------------+ | 结束 | +------------------------------------+ ``` ### 算法说明: 1. **初始化**:设置累加器 `s=0`(引用[5])。 2. **外层循环**:遍历2-100的每个整数 `i`(引用[3])。 3. **因数计数**: - 初始化 `count=0` - 内层循环:用 `j`(1→`i`)检查能否整除 `i` - 若能整除(`i%j==0`),则 `count++` 4. **素数判断**:若 `count==2`(仅被1和自身整除),则累加到 `s` 并输出该素数(引用[2])。 5. **结果输出**:最后输出素数之和 `s`(引用[1])。 ### 优化说明 此基础算法时间复杂度为 $O(n^2)$。实际应用可优化为: 1. 内层循环只需遍历 $j=2 \to \sqrt{i}$(引用[4]) 2. 使用**埃拉托斯特尼筛法**可将复杂度优化至 $O(n \log \log n)$(1亿内计算约800ms) ### 算法验证 - **输出示例**:2, 3, 5,..., 97(共25个素数- **素数之和**:1060(引用[5])
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值