- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
背景:我几年前在学校里第一次学习 C++ 和 Java,但在过去的 9 年左右时间里我没有做过太多编程,因为我以前的职业不需要它。
我决定研究 Project Euler 以温习我的编程并解决了问题 14,该问题要求找到最长 Collatz 序列的 1 到 100 万之间的整数。 (Collatz 序列继续进行,给定一个起始数字,将该数字乘以 3,如果是奇数则加 1,如果是偶数则将其减半。该过程一直持续到数字达到 1。)
我首先使用蛮力解决了这个问题,如下面的代码所示。
int n;
long temp; // long is necessary since some Collatz sequences go outside scope of int
int[] n_length = new int[1000000];
for(n = 0; n < 1000000; n++){
temp = n + 1;
n_length[n] = 1;
while (temp > 1){
n_length[n]++;
if (temp % 2 == 0) temp = temp/2;
else temp = 3*temp + 1;
}
}
int max = 0;
int max_index = 0;
for (int i = 0; i < 1000000; i++){
if (n_length[i] > max){
max = n_length[i];
max_index = i;
}
}
System.out.println("The number with the longest Collatz sequence is " + (max_index + 1));
我认为这种方法效率低下,因为它运行算法的频率明显高于必要的频率。属于前一个数字的 Collatz 序列的任何数字都将有效地确定其序列,因此每次出现在 Collatz 序列中时,您最终都会计算每个数字的序列。
我决定最好在每个数字出现在 Collatz 序列中时将其存储在 map 中,这样您只需计算一次。为此,我使用了一个 TreeMap,将数字用作键,将关联的 Collatz 序列长度用作值,并使用递归函数将每个数字一出现在 Collatz 序列中就插入到映射中。 (请参阅下面的代码。)
public static TreeMap<Long, Integer> tm = new TreeMap<Long, Integer>();
public static void main(String[] args) {
tm.put((long)1, 1);
int maxVal = 1;
long keyWithMaxVal = 1;
int maybeMax;
for (long i = 2; i <= 1000000; i++){
if(!(tm.containsKey(i))){
maybeMax = addKey(i);
if (maybeMax >= maxVal){
maxVal = maybeMax;
keyWithMaxVal = i;
}
}
}
System.out.println("The number with the longest Collatz sequence is " + keyWithMaxVal + " with length " + maxVal);
}
public static int addKey(long key){
while (!(tm.containsKey(key))){
if (key % 2 == 0){
tm.put(key, 1 +addKey(key/2));
}
else{
tm.put(key, 1 + addKey(3*key + 1));
}
}
return tm.get(key);
}
我使用了 TreeMap,因为它会在输入时自动对键进行排序,因此当我遍历 for 循环时,我可以快速检查键是否已经插入,并避免调用 addKey 方法来添加键,除非我必须这样做。我认为这个算法会快得多。
然而,当我实际运行代码时,我惊讶地发现蛮力算法瞬间得出答案,而递归 TreeMap 算法花费的时间要长得多,大约 6 秒。当我将我的程序修改为 500 万而不是 100 万时,差异变得更加明显。我向每个程序添加了一些代码,以确保第二个程序比第一个程序做的工作少,实际上我确定 addKey 方法只为每个键调用一次,而 while 循环需要迭代的次数在第一个程序中等于所有数字 Collatz 序列的长度之和(即比第二个算法中的方法调用次数多得多)。
那么为什么第一个算法比第二个算法快这么多呢?是不是因为第一个算法中的基元数组比第二个算法中的 Wrapper 对象的 TreeMap 需要更少的资源?搜索 map 以检查 key 是否已经存在的速度比我预期的要慢(不应该是记录时间吗?)?需要大量方法调用的递归方法本身就比较慢吗?或者还有其他我忽略的东西
最佳答案
此代码测试为 1 到 500 万之间的数字找到最长的 collatz 序列需要多长时间。它使用三种不同的方法:迭代、递归和将结果存储在 HashMap 中。
输出看起来像这样
iterative
time = 2013ms
max n: 3732423, length: 597
number of iterations: 745438133
recursive
time = 2184ms
max n: 3732423, length: 597
number of iterations: 745438133
with hash map
time = 7463ms
max n: 3732423, length: 597
number of iterations: 15865083
因此对于 HashMap 解决方案,程序必须执行的步骤数减少了近 50 倍。尽管如此,它还是慢了 3 倍以上,我认为主要原因是对数字的简单数学运算,例如添加、乘法等操作比 HashMap 上的操作快得多。
import java.util.function.LongUnaryOperator;
import java.util.HashMap;
public class Collatz {
static int iterations = 0;
static HashMap<Long, Long> map = new HashMap<>();
static long nextColl(long n) {
if(n % 2 == 0) return n / 2;
else return n * 3 + 1;
}
static long collatzLength(long n) {
iterations++;
int length = 1;
while(n > 1) {
iterations++;
n = nextColl(n);
length++;
}
return length;
}
static long collatzLengthMap(long n) {
iterations++;
if(n == 1) return 1;
else return map.computeIfAbsent(n, x -> collatzLengthMap(nextColl(x)) + 1);
}
static long collatzLengthRec(long n) {
iterations++;
if(n == 1) return 1;
else return collatzLengthRec(nextColl(n)) + 1;
}
static void test(String msg, LongUnaryOperator f) {
iterations = 0;
long max = 0, maxN = 0;
long start = System.nanoTime();
for(long i = 1; i <= 5000000; i++) {
long length = f.applyAsLong(i);
if(length > max) {
max = length;
maxN = i;
}
}
long end = System.nanoTime();
System.out.println(msg);
System.out.println("time = " + ((end - start)/1000000) + "ms");
System.out.println("max n: " + maxN + ", length: " + max);
System.out.println("number of iterations: " + iterations);
System.out.println();
}
public static void main(String[] args) {
test("iterative", Collatz::collatzLength);
test("recursive", Collatz::collatzLengthRec);
test("with hash map", Collatz::collatzLengthMap);
}
}
关于java - 欧拉计划 #14 : Why is my TreeMap algorithm slower than brute force?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30270550/
我试图理解两者之间的区别 git push --force 和 git push --force-with-lease 我的猜测是后者只推送到远程如果远程提交了本地分支没有? 最佳答案 force 用
我在将项目发布到本地系统时收到此错误 Copying file obj\Debug\build.force to obj\Release\Package\PackageTmp\obj\Debug\bu
这个例子的描述:http://bl.ocks.org/mbostock/4062045 (见下图),声明它是“带电粒子和 Spring 的物理模拟,使相关 Angular 色更接近。” 我只是好奇该代
请不要标记重复的问题。 大家好, 我正在执行 NSURLAuthenticationMethodClientCertificate,我在其中使用以下代码。如果我不使用 swiftlint,哪个代码没问
我似乎无法删除文件/文件夹,而无需为所有人输入 [A]。我错过了什么? Get-Childitem "C:\Users\*\AppData\Local\Temp\*" -ErrorAction S
我一直在尝试编写在 Streams 上运行的程序和它们的属性,但我觉得即使是最简单的事情我也被困住了。当我在标准库的 Codata/Streams 中查看 repeat 的定义时,我发现了一个我在 A
我正在尝试使用 symfony2 创建一个下载文件的链接。它确实下载了一个文件,但它没有用,因为它是零八位字节。我不知道如何让它工作。有人知道怎么做吗? 文件位于web/uploads/documen
我需要为MySQLd打开网络,但是每次这样做,服务器都会被强行淘汰。一些卑鄙的密码猜测脚本开始在服务器上运行,在端口3306上打开连接并永久尝试随 secret 码。 我该如何阻止这种情况的发生? 对
Azure Functions 是否可以强制通过 HTTPS 进行连接? 我没有在应用程序设置中看到它,也没有看到任何对 Azure Functions 的 web.config 的引用。 最佳答案
我正在使用 Firebird 数据库并正在尝试以下 sql,但每次它返回 0,而不是 0.61538(等等)。 SELECT (COUNT(myfield)/26) totalcount FROM m
就目前情况而言,这个问题不太适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我想要一个永远未定义的属性: var foo = {bar:undefined} 如果有人稍后尝试更改属性栏,那么它也应该导致未定义。 foo.bar = 'someValue'// foo.bar/
我有课,Target无法更改,具有通用约束。我想从没有约束的泛型类中构建该类的实例。下面演示了我想要做的事情的意图,但我意识到这段代码将无法编译并且 typeof(T).IsClass是运行时检查,通
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
假设我在包中编写了一个类,名为 mypackage.myclass。我已经为包和类编写了自己的 HTML 文档,并将其包含在 MATLAB 帮助浏览器中,如 the MATLAB documentat
我们有一个多平台项目,它为几个平台生成二进制文件,比如 mac、windows、linux...是否可以强制 git 将所有文件的编码更改为某个特定平台(例如:Linux)。那么如何在每次用户提交或推
我正在使用 MSBuild 自动为标签创建一个文本文件和一个 ZIP 文件。我的 MSBuild 项目由 CruiseControl.NET 调用. 文本文件总是latest.txt,ZIP 文件是(
根据我的一些 API 规范: Force to place an Auth transaction into the current batch (PostAuth) or to place a tr
我正在使用超集 0.20.4 如果我想在我的 URL 中添加一个 token 以自动登录到特定用户超集/仪表板/3?standalone=true&token=123456789 我应该在代码的哪个位
我有一个大问题:我有一个 listview,每个项目都链接到页面 #concorsi。当我单击链接时,URL 会变为 #concorsi?numero=1,因为我正在从 JSON 中获取表单编号 1。
我是一名优秀的程序员,十分优秀!