Java 中埃拉托斯特尼的并行筛法

2024-01-02

我正在尝试并行实现埃拉托斯特尼筛法。我创建了一个布尔列表,其中填充了给定大小的 true 值。每当找到素数时,该素数的所有倍数都会在布尔列表中标记为 false。

我尝试使该算法并行的方法是启动一个新线程,同时仍然过滤初始素数。例如,该算法以 prime = 2 开始。在过滤器的 for 循环中,当 prime * prime 时,我创建另一个 for 循环,其中检查 prime (2) 和 prime * prime (4) 之间的每个数字。如果布尔列表中的索引仍然为真,我会启动另一个线程来过滤该素数。

随着要过滤的素数不断进行,嵌套 for 循环会产生越来越多的开销,因此我将其限制为仅在素数

我的代码如下所示:

主要类别:

public class Main {
    private static ListenableQueue<Integer> queue = new ListenableQueue<>(new LinkedList<>());
    private static ArrayList<Integer> primes = new ArrayList<>();
    private static boolean serialList[];
    private static ArrayList<Integer> serialPrimes = new ArrayList<>();
    private static ExecutorService exec = Executors.newFixedThreadPool(10);
    private static int size = 100000000;
    private static boolean list[] = new boolean[size];
    private static int lastPrime = 2;

    public static void main(String[] args) {
        Arrays.fill(list, true);

        parallel();
    }

    public static void parallel() {
        Long startTime = System.nanoTime();
        int firstPrime = 2;

        exec.submit(new Runner(size, list, firstPrime));
    }

    public static void parallelSieve(int size, boolean[] list, int prime) {
        int queuePrimes = 0;
        for (int i = prime; i * prime <= size; i++) {
            try {
                list[i * prime] = false;
                if (prime < 100) {
                    if (i == prime * prime && queuePrimes <= 1) {
                        for (int j = prime + 1; j < i; j++) {
                            if (list[j] && j % prime != 0 && j > lastPrime) {
                                lastPrime = j;
                                startNewThread(j);
                                queuePrimes++;
                            }
                        }
                    }
                }
            } catch (ArrayIndexOutOfBoundsException ignored) { }
        }
    }

    private static void startNewThread(int newPrime) {
        if ((newPrime * newPrime) < size) {
            exec.submit(new Runner(size, list, newPrime));
        }
        else {
            exec.shutdown();
            for (int i = 2; i < list.length; i++) {
                if (list[i]) {
                    primes.add(i);
                }
            }
        }
    }
}

跑步者类别:

public class Runner implements Runnable {
    private int arraySize;
    private boolean[] list;
    private int k;

    public Runner(int arraySize, boolean[] list, int k) {
        this.arraySize = arraySize;
        this.list = list;
        this.k = k;
    }

    @Override
    public void run() {
        Main.parallelSieve(arraySize, list, k);
    }

}

我觉得有一个更简单的方法可以解决这个问题...... 你们对我如何使这种并行化工作有什么建议,也许更简单一些?


创造一个表演者同时实现像埃拉托斯特尼筛法这样的算法比创建高性能的单线程实现要困难一些。原因是您需要找到一种方法来划分工作,以最大限度地减少并行工作线程之间的通信和干扰。

如果实现完全隔离,那么您可以希望速度提高到接近可用逻辑处理器的数量,或者在典型的现代 PC 上大约提高一个数量级。相比之下,使用一个不错的单线程实现的筛子将使您的速度提高至少两到三个数量级。一个简单的逃避方法是在需要时简单地从文件中加载数据,或者使用像 Kim Walisch 的类似的优质筛选程序总理筛 https://github.com/kimwalisch/primesieve.

即使我们只想研究并行化问题,仍然有必要对算法本身及其运行的机器有一些了解。

最重要的方面是,现代计算机具有较深的缓存层次结构,其中只有 L1 缓存(通常为 32 KB)可以全速访问,而所有其他内存访问都会产生严重的损失。转换为埃拉托斯特尼筛法,这意味着您需要一次对目标范围进行 32 KB 窗口的筛选,而不是跨过许多兆字节来跨越每个质数。在平行舞蹈开始之前,必须筛选直到目标范围末端的平方根的小素数,但随后可以独立筛选每个段或窗口。

筛选给定的窗口或段需要确定要筛选的小素数的起始偏移量,这意味着每个窗口和除法对每个小素数至少进行一次模除法是一项极其缓慢的操作。但是,如果您筛选连续的段而不是放置在范围内任何位置的任意窗口,那么您可以保留向量中每个素数的结束偏移量,并将它们用作下一段的起始偏移量,从而消除起始偏移量的昂贵计算。

因此,埃拉托斯特尼筛法的一种有前途的并行化策略是为每个工作线程提供一组连续的 32 KB 块进行筛选,这样每个工作线程只需进行一次起始偏移计算。这样,工作人员之间就不会出现内存访问争用,因为每个工作人员都有自己独立的目标范围子范围。

然而,在开始并行化之前——即让你的代码变得更复杂——你应该首先精简它,并将需要完成的工作减少到绝对必要的程度。例如,看一下代码中的这个片段:

for (int i = prime; i * prime <= size; i++)
   list[i * prime] = false;

不要在每次迭代中重新计算循环边界并使用乘法进行索引,而是根据预先计算的循环不变值检查循环变量并将乘法减少为迭代加法:

for (int o = prime * prime; o <= size; o += prime)
   list[o] = false;

有两种简单的特定于筛子的优化可以提供显着的快艇。

1) 将偶数从筛子中剔除,并在需要时凭空拉出质数 2。宾果,你的表现刚刚提高了一倍。

2) 不要用小奇素数 3、5、7 等筛选每个段,而是在该段(甚至整个范围)上喷射预先计算的模式。这节省了时间,因为这些小素数在每个部分中进行了很多很多步骤,并且占据了筛分时间的大部分。

还有更多可能的优化,包括一些更容易实现的目标,但要么回报递减,要么努力曲线急剧上升。尝试搜索代码审查 https://codereview.stackexchange.com/为“筛子”。另外,不要忘记,除了算法问题和机器架构之外,您还要与 Java 编译器作斗争,即诸如数组边界检查之类的事情,您的编译器可能无法将其从循环中提升出来。

给您一个大概的数字:具有预先计算模式的单线程分段仅赔率筛选器可以在 C# 中在 2 到 4 秒内筛选整个 32 位范围,具体取决于除了上述内容之外您还应用了多少 TLC。在我老化的笔记本上,您的素数问题要小得多,直到 100000000 (1e8),不到 100 毫秒就得到了解决。

下面的一些代码显示了窗口筛选的工作原理。为了清楚起见,我在读出素数时放弃了所有优化,例如仅赔率表示或轮 3 步进等。它是 C#,但应该与 Java 足够相似以便可读。

注意:我称之为筛数组eliminated因为 true 值表示一个被划掉的数字(可以避免在开始时用所有 true 填充数组,而且无论如何它都更符合逻辑)。

static List<uint> small_primes_between (uint m, uint n)
{
    m = Math.Max(m, 2);

    if (m > n)
        return new List<uint>();

    Trace.Assert(n - m < int.MaxValue);

    uint sieve_bits = n - m + 1;
    var eliminated = new bool[sieve_bits];

    foreach (uint prime in small_primes_up_to((uint)Math.Sqrt(n)))
    {
        uint start = prime * prime, stride = prime;

        if (start >= m)
            start -= m;
        else
            start = (stride - 1) - (m - start - 1) % stride;

        for (uint j = start; j < sieve_bits; j += stride)
            eliminated[j] = true;
    }

    return remaining_numbers(eliminated, m);
}

//---------------------------------------------------------------------------------------------

static List<uint> remaining_numbers (bool[] eliminated, uint sieve_base)
{
    var result = new List<uint>();

    for (uint i = 0, e = (uint)eliminated.Length; i < e; ++i)
        if (!eliminated[i])
            result.Add(sieve_base + i);

    return result;
}

//---------------------------------------------------------------------------------------------

static List<uint> small_primes_up_to (uint n)
{
    Trace.Assert(n < int.MaxValue);    // size_t is int32_t in .Net (!)

    var eliminated = new bool[n + 1];  // +1 because indexed by numbers

    eliminated[0] = true;
    eliminated[1] = true;

    for (uint i = 2, sqrt_n = (uint)Math.Sqrt(n); i <= sqrt_n; ++i)
        if (!eliminated[i])
            for (uint j = i * i; j <= n; j += i)
                eliminated[j] = true;

    return remaining_numbers(eliminated, 0);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中埃拉托斯特尼的并行筛法 的相关文章

  • 以相反的顺序打印任何集合中的项目?

    我在 使用 Java 进行数据结构和问题解决 一书中遇到以下问题 编写一个例程 使用 Collections API 以相反的顺序打印任何 Collection 中的项目 不要使用 ListIterator 我不会把它放在这里 因为我想让有
  • 使用 Exec Maven 插件分叉 Java,而不使用“exec”目标

    来自文档 https www mojohaus org exec maven plugin exec exec在单独的进程中执行程序和Java程序 exec java在同一虚拟机中执行 Java 程序 我想 fork 一个 java 程序
  • 通过Zuul上传大文件

    我在通过 zuul 上传大文件时遇到问题 我正在使用 apache commons 文件上传 https commons apache org proper commons fileupload https commons apache o
  • 如何使用 Java 引用释放 Java Unsafe 内存?

    Java Unsafe 类允许您按如下方式为对象分配内存 但是使用此方法在完成后如何释放分配的内存 因为它不提供内存地址 Field f Unsafe class getDeclaredField theUnsafe Internal re
  • 如何使用 Java Apache POI 隐藏 Excel 工作表中以下未使用的行?

    我正在使用数据库中的数据填充模板 Excel 工作表 for Map
  • JAXB - 忽略元素

    有什么方法可以忽略 Jaxb 解析中的元素吗 我有一个很大的 XML 文件 如果我可以忽略其中一个大而复杂的元素 那么它的解析速度可能会快很多 如果它根本无法验证元素内容并解析文档的其余部分 即使该元素不正确 那就更好了 例如 这应该只生成
  • ThreeTen 向后移植与 JSR-310 的比较

    由于某些原因 我们现在无法使用 java 8 我们仍然停留在 java 7 上 不过 我想使用新的JSR 310 date time APIs现在 使用官方向后移植 ThreeTen http www threeten org threet
  • Mockito 和 Hamcrest:如何验证 Collection 参数的调用?

    我遇到了 Mockito 和 Hamcrest 的泛型问题 请假设以下界面 public interface Service void perform Collection
  • 为什么在将 String 与 null 进行比较时会出现 NullPointerException?

    我的代码在以下行中出现空指针异常 if stringVariable equals null 在此语句之前 我声明了 stringVariable 并将其设置为数据库字段 在这个声明中 我试图检测该字段是否有null值 但不幸的是它坏了 有
  • 如何更改 Swagger-ui URL 前缀?

    我正在使用 Springfox Swagger2 和 Spring boot 1 5 9 我可以通过此链接访问 swagger UI http localhost 8090 swagger ui html http localhost 80
  • Android 认为我没有关闭数据库!为什么?

    我有一个 SQLiteDatabase 数据成员 我在 onCreate 中初始化它 并在 onPause onStop 和 onDestroy 中调用 close 它在 onResume 中重新初始化 它似乎运行得很好 但当我查看调试器时
  • 无需递归即可对可观察结果进行分页 - RxJava

    我有一个非常标准的 API 分页问题 您可以通过一些简单的递归来处理 这是一个捏造的例子 public Observable
  • 如何自定义舍入形式

    我的问题可能看起来很简单 但仍然无法得到有效的东西 我需要自定义 Math round 舍入格式或其他格式以使其工作如下 如果数字是 1 6 他应该四舍五入到 1 如果大于或等于 1 7 他应该四舍五入到 2 0 对于所有其他带有 6 的小
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • 如何向页面添加 HTML 页眉和页脚?

    如何使用 itext 从 html 源添加标题到 pdf 目前 我们已经扩展了 PdfPageEventHelper 并重写了这些方法 工作正常 但当我到达 2 个以上页面时 它会抛出 RuntimeWorkerException Over
  • 文本视图不显示全文

    我正在使用 TableLayout 和 TableRow 创建一个简单的布局 其中包含两个 TextView 这是代码的一部分
  • java实现excel价格、收益率函数[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 使用 DBCP 配置 Tomcat

    在闲置一段时间 几个小时 后 我们收到了 CommunicationsException 来自 DBCP 错误消息 在异常中 位于这个问题的末尾 但我没有看到任何配置文件中定义的 wait timeout 我们应该看哪里 在 tomcat
  • 在会话即将到期之前调用方法

    我的网络应用程序有登录的用户 有一个超时 在会话过期之前 我想执行一个方法来清理一些锁 我已经实现了sessionListener但一旦我到达public void sessionDestroyed HttpSessionEvent eve
  • 关闭扫描仪是否会影响性能

    我正在解决一个竞争问题 在问题中 我正在使用扫描仪获取用户输入 这是 2 个代码段 一个关闭扫描器 一个不关闭扫描器 关闭扫描仪 import java util Scanner public class JImSelection publ

随机推荐

  • 如何在共享托管环境中实现ASP.Net MVC网站的后台处理?

    我正在使用 ASP Net MVC 开发我的第一个 Web 应用程序 我现在的情况是希望有一个后台服务来处理应用程序外部的状态通知 这与 stackoverflow 上的声誉 徽章系统不同 处理这样的事情最好的方法是什么 在像我正在使用的
  • Anylogic 中的时间分布和处理所花费的时间

    我正在研究生产模型 其中原材料的输入按小时计算 我运行该模型 8 小时 1 个班次 所以基本上有 16 小时资源闲置 当我没有使用计划部分并运行模型 8 7 小时 56 小时 时 每个作业的时间测量都很好 但现在当我计划输出时 它也包括空闲
  • 用户没有访问该对象的权限。 Firebase 存储 Android

    我是 firebase 存储的新手 为了学习它 我制作了一个简单的应用程序 其中有一个按钮和一个ImageView 当我点击按钮时 会出现一张图片 from drawable 显示在 ImageView 上 我还编写了在Firebase上上
  • 部署 Gen2 Cloud Function 时出现权限被拒绝错误

    我们根据给定的需求开发了云功能 并使用第一代进行了初步验证 一切顺利 但需要进行的修改很少 因此需要额外的处理时间 因此我们必须切换到 gen2 以下是 gcloud 函数部署命令 gcloud functions deploy gen2
  • ServiceStack Swagger UI 和 API 版本号

    有没有办法将版本号输入到 swagger UI 中 那么我们可以让开发者知道每个部署是什么版本吗 这在最新版本的 ServiceStack 中是可能的 您只需在 AppHost 中设置 ServiceStack API 版本即可 publi
  • 根据内容将输入拆分为多个输出?

    假设有一个如下所示的文件 xxxx aa whatever yyyy bb whatever zzzz aa whatever 我想将其分成 2 个文件 其中包含 first xxxx aa whatever zzzz aa whateve
  • 如何迭代 getElementByClassName 返回

    我读过几篇关于 GetElementsByClassName 的文章 但我很难理解如何迭代它返回的内容 我正在编写纯 JavaScript 代码 以便当用户滚动时我的导航栏将采用 固定 位置 但是 当发生此更改时 导航栏列表项需要更改格式
  • 如何获取jenkins管道插件作业的工作区(WorkflowRun对象java API)

    在java API中 我可以从Run java对象访问工作空间路径 直到今天 所有对象都是 hudson model AbstractBuild 的实例 hudson model AbstractBuild getWorkspace hud
  • 内存屏障/栅栏的开销

    我目前正在编写 C 代码 并在代码中使用大量内存屏障 栅栏 我知道 MB 告诉编译器和硬件不要重新排序围绕它的写入 读取 但我不知道这个操作对于处理器在运行时有多复杂 我的问题是 这种屏障的运行时开销是多少 我用谷歌没有找到任何有用的答案
  • pymongo:删除重复项(映射减少?)

    我确实有一个包含多个集合的数据库 总共约 1500 万个文档 文档如下所示 简化 Text blabla ID 101 Text Whuppppyyy ID 102 Text Abrakadabraaa ID 103 Text olalal
  • Gorm 一对多搜索

    有以下问题 我有 Bookmaker 和 Users 域类 一个博彩公司有许多用户 class Bookmaker static hasMany users User 并且 User 域类不包含对 Bookmaker 的引用 我的目标是创建
  • 来自 JSON 字符串的打字稿“enum”

    有没有办法让 TypeScript 枚举与 JSON 中的字符串兼容 例如 enum Type NEW OLD interface Thing type Type let thing Thing JSON parse type NEW al
  • Python 3 中 str.translate 的自定义表

    如果我运行这段代码 s translate str maketrans as dfg 1234 qw 我会得到 ValueError string keys in translate table must be of length 1 有没
  • 获取控制台句柄

    如何获取外部应用程序的控制台句柄 我有一个程序作为控制台运行 我有一个第二个程序将调用 GetConsoleScreenBufferInfo 但为此我需要第一个程序的控制台句柄 给定第一个程序的 HWND 是否有可能我可以获得它的控制台句柄
  • 使用 withColumnRenamed 重命名多列

    我想使用 Spark withColumnRenamed 函数更改两列的名称 当然 我可以写 data sqlContext createDataFrame 1 2 3 4 x1 x2 data data withColumnRenamed
  • 如何使用 codeigniter 锁定表?

    我必须在模型中运行这个 sql 例程 this gt db gt query LOCK TABLE orders WRITE this gt db gt query TRUNCATE TABLE orders this gt db gt q
  • 从 master 分支构建到 gh-pages 分支

    我想做的事 我正在使用github 我有两个分支机构 主页面和 gh 页面 我的 master 分支上有一个 unity3d 项目 当我运行它时 它将生成一个网页 我想在 gh pages 分支上显示网页的内容 我认为这意味着我必须在存储库
  • 如何通过区分类型来隔离枚举?

    下面的代码定义了两个枚举 class Insect BEE 0x00 WASP 0x01 BUMBLEBEE 0x02 class Breakfast HAM 0x00 EGGS 0x01 PANCAKES 0x02 b Insect WA
  • 如何根据 R 中其他行和列中的值来填充数据框

    假设我有一个如下所示的数据框 ID T X Y Z 1 1 A A NA 1 2 B A NA 1 3 B B NA 1 4 B A NA 2 1 A B NA 2 2 A A NA 2 3 B A NA 2 4 A B NA 3 1 B
  • Java 中埃拉托斯特尼的并行筛法

    我正在尝试并行实现埃拉托斯特尼筛法 我创建了一个布尔列表 其中填充了给定大小的 true 值 每当找到素数时 该素数的所有倍数都会在布尔列表中标记为 false 我尝试使该算法并行的方法是启动一个新线程 同时仍然过滤初始素数 例如 该算法以