所有Primes的总和低于2000000 - 问题10

本文介绍了使用Eratosthenes筛法解决找出200万以下所有素数总和的问题,探讨了数据表示、消除偶数的优化策略,并提及了Atkin筛法。提供了代码实现和优化,强调了算法在寻找素数中的重要性。
摘要由CSDN通过智能技术生成

所有Primes的总和低于2000000 - 问题10

本博客文章是关于解决问题10项目欧拉。就像问题7一样,问题就在于素数问题。我为问题7发布解决方案策略也适用于此问题。

问题在于

低于10的素数之和为2 + 3 + 5 + 7 = 17。

找出200万以下所有素数的总和。

这个问题没有什么特别棘手的问题,因为没有找到所有素数的公式,我们将不得不蛮力解决问题。然而,蛮力解决方案可以或多或少地优雅。在我提供的源代码中,您可以找到问题7中的解决方案,该问题已采用。我不会在这种方法上花费更多时间,而是向你介绍Eratosthenes的Sieve。

Eratosthenes的筛子

埃拉托色尼筛是顾名思义的发明埃拉托色尼谁是希腊数学家生活公元前200年。

该算法需要具有要找到的素数的上限。让我们称这个限制为N.

该算法的工作原理如下。

  1. 创建一个连续整数列表l {2,3,...,N}。
  2. 选择p作为列表中的第一个素数,p = 2。
  3. l中删除所有p的倍数。
  4. set p等于l中尚未删除的下一个整数。
  5. 重复步骤3和4,直到2 > N,列表中的所有剩余数字都是素数

这是一个非常简单的算法,维基百科上的描述有一个非常好的图形插图,我认为我不能做得更好。所以去看看吧。算法找到素数,然后标记该素数的所有倍数。第一个新数字将永远是素数,因为所有未编号的数字都将被删除。

实现算法可能非常简单,挑战绝对是优化执行和内存的实现。

我发现了一个实现两个实现,它们与Stack OverflowdigitalBush类似,看起来非常有前景。然后我进一步优化了一下。优化代码如下所示

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

public int[] ESieve(int upperLimit) {

    int sieveBound = (int)(upperLimit - 1) / 2;

    int upperSqrt = ((int)Math.Sqrt(upperLimit) - 1) / 2;

 

    BitArray PrimeBits = new BitArray(sieveBound + 1, true);

 

    for (int i = 1; i <= upperSqrt; i++) {

        if (PrimeBits.Get(i)) {

            for (int j = i * 2 * (i + 1); j <= sieveBound; j += 2 * i + 1) {

                PrimeBits.Set(j, false);

            }

        }

    }

 

    List numbers = new List((int)(upperLimit / (Math.Log(upperLimit) - 1.08366)));

    numbers.Add(2);

 

    for (int i = 1; i <= sieveBound; i++) {

        if (PrimeBits.Get(i)) {

            numbers.Add(2 * i + 1);

        }

    }

 

    return numbers.ToArray();

}

一旦我们得到了所有素数的列表,我们需要其余的代码是一个简单的循环汇总数组。您可以检查该位的源代码。在以下部分中,我将介绍代码的不同方面。

数据表示

它使用BitArray存储所有数字。它是一个可枚举的类型,每个布尔值使用一位。根据此讨论,使用BitArray意味着算法将内存使用量限制为与布尔数组相比的因子32 。但是,它会降低运营性能。我们需要一个数组来容纳2.000.000个数字,这意味着相差250kB和8MB。

我玩了一下,并没有看到一小部分素数的性能差异。对于大量的素数,我注意到BitArray稍快一点。这可能是由于更好的缓存优化,因为BitArray更容易存储在CPU缓存中,从而提高了性能。

消除偶数

digitalBush,他通过跳过循环中的偶数来优化他的代码。我选择了另一种方法,以避免为它分配内存。它只需要跟踪我的索引。

基本上我想从三开始,然后对于一个计数器i = {1,2,...,N / 2}表示每个奇数p = {3,5,7,...,N}。这可以在p = 2i + 1时完成。这就是我在代码中所做的。它使代码更复杂,但节省了一半的内存,因此它可以处理更大的集合。

此外,我们在p 2 =(2i + 1)(2i + 1)= 4i 2 + 4i + 1 开始我们的内部循环,它将具有索引2i(i + 1),这是我们开始搜索内部的地方环。通过在内循环的每次迭代中增加p = 2i + 1个索引,我跳跃2p,因此仅取p的奇数倍。由于乘以偶数将得到均匀的结果,因此不是素数。

阿特金筛子

生成素数的另一种筛选方法是Atkin筛选。它应该比Eratosthenes筛更快。我已经对它进行了参考实现,但是我无法理解如何优化它,所以我从来没有能够像Eratosthenes的Sieve那样优化它。但是,我在源代码中包含了参考实现,因此如果您愿意,可以使用它。

包起来

这篇文章采取了一些不同的方法。我努力工作以真正优化代码。因为我相信用于寻找素数的Sieve方法将在以后使用。因此,制作可重复使用的快速代码,将使以后的问题更容易解决。

我采用了三种不同的方法,并试图优化它们。这三个结果是

1

2

3

4

6

Prime sum of all primes below 2000000 is 142913828922

Solution took 203,125 ms using Trial Division

Prime sum of all primes below 2000000 is 142913828922

Solution took 31,25 ms using Sieve of Eratosthenes

Prime sum of all primes below 2000000 is 142913828922

Solution took 31,25 ms using Sieve of Atkin

对于如此小的数字,两种筛分方法之间的差异并不明显,但如果我们开始计算低于10亿的所有质数,则会显示执行时间的差异。像往常一样,您可以下载源代码

如果您能够进一步优化筛选方法,我将非常感谢您发表评论。我总是渴望学习更多知识并进一步加快速度。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值