- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
在过去的几天里(更多是从算法而非数学角度)我对调查给定数字的 Hailstone 序列 (Collatz conjecture) 的长度特别感兴趣。实现递归算法可能是计算长度的最简单方法,但在我看来这是一种不必要的计算时间浪费。许多序列重叠;以 3 的 Hailstone 序列为例:
3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
长度为 7;更具体地说,需要 7 次操作才能达到 1。如果我们再进行 6 次操作:
6 -> 3 -> ...
我们立即注意到我们已经计算过了,所以我们只需添加 3 的序列长度,而不是再次遍历所有这些数字,从而大大减少了计算每个数字的序列长度所需的操作次数。
我尝试使用 HashMap 在 Java 中实现它(考虑到 O(1) 概率获取/放置复杂性,这似乎是合适的):
import java.util.HashMap;
/* NOTE: cache.put(1,0); is called in main to act as the
* 'base case' of sorts.
*/
private static HashMap<Long, Long> cache = new HashMap<>();
/* Returns length of sequence, pulling prerecorded value from
* from cache whenever possible, and saving unrecorded values
* to the cache.
*/
static long seqLen(long n) {
long count = 0, m = n;
while (true) {
if (cache.containsKey(n)) {
count += cache.get(n);
cache.put(m, count);
return count;
}
else if (n % 2 == 0) {
n /= 2;
}
else {
n = 3*n + 1;
}
count++;
}
}
seqLen
本质上要做的是从一个给定的数字开始,遍历该数字的 Hailstone 序列,直到它遇到一个已经在 cache
中的数字,在这种情况下它会将其添加到 count
的当前值,然后将值和关联的序列长度作为 (key,val)
对记录在 HashMap 中。
我还有以下相当标准的递归算法用于比较:
static long recSeqLen(long n) {
if (n == 1) {
return 0;
}
else if (n % 2 == 0) {
return 1 + recSeqLen(n / 2);
}
else return 1 + recSeqLen(3*n + 1);
}
从各方面来看,日志算法应该比朴素的递归方法运行得快很多。然而,在大多数情况下,它根本不会运行得那么快,对于较大的输入,它实际上运行得较慢。运行以下代码产生的时间随着 n
大小的变化而有很大差异:
long n = ... // However many numbers I want to calculate sequence
// lengths for.
long st = System.nanoTime();
// Iterative logging algorithm
for (long i = 2; i < n; i++) {
seqLen(i);
}
long et = System.nanoTime();
System.out.printf("HashMap algorithm: %d ms\n", (et - st) / 1000000);
st = System.nanoTime();
// Using recursion without logging values:
for (long i = 2; i < n; i++) {
recSeqLen(i);
}
et = System.nanoTime();
System.out.printf("Recusive non-logging algorithm: %d ms\n",
(et - st) / 1000000);
n = 1,000
:两种算法均为 ~2msn = 100,000
:~65ms 用于迭代日志记录,~75ms 用于递归非日志记录n = 1,000,000
:~500 毫秒和~900 毫秒n = 10,000,000
:~14,000 毫秒和~10,000 毫秒在更高的值下我会遇到内存错误,所以我无法检查模式是否继续。
所以我的问题是:为什么对于大的 n 值,日志记录算法突然开始比朴素递归算法花费更长时间?
完全废弃 HashMap 并选择简单的数组结构(以及删除检查值是否在数组中的部分开销)产生所需的效率:
private static final int CACHE_SIZE = 80000000;
private static long[] cache = new long[CACHE_SIZE];
static long seqLen(long n) {
int count = 0;
long m = n;
do {
if (n % 2 == 0) {
n /= 2;
}
else {
n = 3*n + 1;
}
count++;
} while (n > m);
count += cache[(int)n];
cache[(int)m] = count;
return count;
}
迭代整个缓存大小(8000 万)现在只需 3 秒,而使用递归算法需要 93 秒。 HashMap 算法会抛出内存错误,因此它甚至无法进行比较,但考虑到它在较低值时的行为,我感觉它不会很好地进行比较。
最佳答案
即兴发挥,我猜它会花费大量时间重新分配 HashMap 。听起来你是从空开始的,然后不断地往里面加东西。这意味着随着它的大小增加,它将需要分配更大的内存块来存储您的数据,并重新计算所有元素的哈希值,即 O(N)。尝试将大小预先分配给您希望放入其中的大小。参见 https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html进行更多讨论。
关于java - 内存效率问题(Collatz Hailstone 序列),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33404821/
第一个 .on 函数比第二个更有效吗? $( "div.container" ).on( "click", "p", function(){ }); $( "body" ).on( "click",
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 已关闭 7 年前。 Improve
我有这样的查询: $('#tabContainer li'); JetBrains WebStorm IDE 将其突出显示为低效查询。它建议我改用这个: $('#tabContainer').find
我刚刚在 coursera ( https://www.coursera.org/saas/) 上听了一个讲座,教授说 Ruby 中的一切都是对象,每个方法调用都是在对象上调用发送方法,将一些参数传递
这可能是用户“不喜欢”的另一个问题,因为它更多的是与建议相关而不是与问题相关。 我有一个在保存和工作簿打开时触发的代码。 它在 f(白天与夜晚,日期与实际日期)中选择正确的工作表。 周一到周三我的情况
这只是我的好奇心,但是更有效的是递归还是循环? 给定两个功能(使用通用lisp): (defun factorial_recursion (x) (if (> x 0) (*
这可能是一个愚蠢的问题,但是while循环的效率与for循环的效率相比如何?我一直被教导,如果可以使用for循环,那我应该这样做。但是,实际上之间的区别是什么: $i = 0; while($i <
我有一个Elasticsearch索引,其中包含几百万条记录。 (基于时间戳的日志记录) 我需要首先显示最新记录(即,按时间戳降序排列的记录) 在时间戳上排序desc是否比使用时间戳的函数计分功能更有
使用Point2D而不是double x和y值时,效率有很大差异吗? 我正在开发一个程序,该程序有许多圆圈在屏幕上移动。他们各自从一个点出发,并越来越接近目的地(最后,他们停下来)。 使用 .getC
我正在编写一个游戏,并且有一个名为 GameObject 的抽象类和三个扩展它的类(Player、Wall 和 Enemy)。 我有一个定义为包含游戏中所有对象的列表。 List objects; 当
我是 Backbone 的初学者,想知道两者中哪一个更有效以及预期的做事方式。 A 型:创建一个新集合,接受先前操作的结果并从新集合中提取 key result = new Backbone.Coll
最近,关于使用 LIKE 和通配符搜索 MS SQL 数据库的最有效方法存在争论。我们正在使用 %abc%、%abc 和 abc% 进行比较。有人说过,术语末尾应该始终有通配符 (abc%)。因此,根
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想知道,这样做会更有效率吗: setVisible(false) // if the component is invisible 或者像这样: if(isVisible()){
我有一个静态方法可以打开到 SQL Server 的连接、写入日志消息并关闭连接。我在整个代码中多次调用此方法(平均每 2 秒一次)。 问题是 - 它有效率吗?我想也许积累一些日志并用一个连接插入它们
这个问题在这里已经有了答案: Best practice to avoid memory or performance issues related to binding a large numbe
我为我的 CS 课(高中四年级)制作了一个石头剪刀布游戏,我的老师给我的 shell 文件指出我必须将 do while 循环放入运行者中,但我不明白为什么?我的代码可以工作,但她说最好把它写在运行者
我正在编写一个需要通用列表的 Java 应用程序。该列表需要能够经常动态地调整大小,对此的明显答案是通用的Linkedlist。不幸的是,它还需要像通过调用索引添加/删除值一样频繁地获取/设置值。 A
我的 Mysql 语句遇到了真正的问题,我需要将几个表连接在一起,查询它们并按另一个表中值的平均值进行排序。这就是我所拥有的... SELECT ROUND(avg(re.rating
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Is there a difference between i==0 and 0==i? 以下编码风格有什么
我是一名优秀的程序员,十分优秀!