为什么这不是在恒定的空间中运行(我如何做到这一点)?

时间:2010-06-20 09:47:35

标签: clojure

我正在做Project Euler来学习Clojure。

此函数的目的是计算从1m的整数集的lcm。

(lcm 10)返回2520

这是一种相当蛮力的方式。从理论上讲,我们会检查从m到无穷大的每个数字,并返回第一个数字,所有值1m均匀地除以该数字。

如果我理解'懒惰'意味着什么(如果我真的在这里懒惰),那么这应该在恒定的空间中运行。我们无需保留从1m的数字列表以及我们循环播放的无限数字中的1个值。

然而,我java.lang.OutOfMemoryError: Java heap space的{​​{1}}值大于17。

m

谢谢!

4 个答案:

答案 0 :(得分:11)

据我所知,你的代码实际上是懒惰的(在某种意义上说,它并不急于得到答案...... ;-) - 见下文),但它会堆积成堆垃圾只需考虑(lvm 17)就要求在(range 1 18)上进行超过120万次的延迟过滤操作。我不能重现你的内存不足问题,但我暂时猜想它可能是你的记忆和问题的一个问题。 GC设置。

现在虽然我意识到你的问题实际上并不是关于算法的,但请注意所有那些垃圾的产生,所有那些过滤操作的执行等不仅彻底破坏了空间复杂性,而且时间的复杂性如同好。为什么不使用实际的LCM算法?就像利用lcm(X) = gcd(X) / product(X) X一组自然数一样。可以使用Euclid算法计算GCD。

(defn gcd
  ([x y]
     (cond (zero? x) y
           (< y x)   (recur y x)
           :else     (recur x (rem y x))))
  ([x y & zs]
     (reduce gcd (gcd x y) zs)))

(defn lcm
  ([x y] (/ (* x y) (gcd x y)))
  ([x y & zs]
     (reduce lcm (lcm x y) zs)))

完成上述操作后,(apply lcm (range 1 18))会在短时间内给您答案。

答案 1 :(得分:4)

我在Clojure 1.1上获得相同的OutOfMemoryError,但在1.2上没有。

我想这是1.1中的一个错误,其中for持有的垃圾超过了必要的数量。

所以我认为修复是升级Clojure。或者在很短的时间内使用Michal的算法得到答案。

答案 2 :(得分:3)

虽然我承认这被认为是蛮力,但我对这个想法感到不满。对于最多运行50的连续数字集,lcm是3099044504245996706400。你真的想要一个循环来测试到那个点的每个数字来识别集合的lcm吗?

其他方案似乎要好得多。例如,计算序列的每个成员,然后简单地计算每个素因子的最大出现次数。或者,构建一个简单的主筛,同时对数字集进行因子分析,同时允许您计算因子多重性。

这些方案可以编写为高效。或者你可以使用蛮力。后者在这里看起来很傻。

答案 3 :(得分:0)

Michal对这个问题是正确的。筛子会快一点,因为不需要gcd计算:

编辑:这段代码实际上是非常错误的。我把它留在这里是为了提醒自己,如果我有这样的宿醉,请检查我的工作两次。

(ns euler (:use clojure.contrib.math))

(defn sieve 
  ([m] (sieve m (vec (repeat (+ 1 m) true)) 2))
  ([m sieve-vector factor] 
       (if (< factor m) 
       (if (sieve-vector factor)
           (recur m 
              (reduce #(assoc %1 %2 false)
                  sieve-vector
                  (range (* 2 factor) (+ 1 m) factor))
              (inc factor))
           (recur m sieve-vector (inc factor)))
       sieve-vector)))

(defn primes [m] (map key (filter val (seq (zipmap (range 2 m) (subvec (sieve m)  2))))))

(defn prime-Powers-LCM [m] (zipmap (primes m) (map #(quot m %) (primes m))))

(defn LCM [m] (reduce #(* %1 (expt (key %2) (val %2))) 1 (seq (prime-Powers-LCM m))))