我正在做Project Euler来学习Clojure。
此函数的目的是计算从1
到m
的整数集的lcm。
(lcm 10)
返回2520
这是一种相当蛮力的方式。从理论上讲,我们会检查从m
到无穷大的每个数字,并返回第一个数字,所有值1
到m
均匀地除以该数字。
如果我理解'懒惰'意味着什么(如果我真的在这里懒惰),那么这应该在恒定的空间中运行。我们无需保留从1
到m
的数字列表以及我们循环播放的无限数字中的1个值。
然而,我java.lang.OutOfMemoryError: Java heap space
的{{1}}值大于17。
m
谢谢!
答案 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))))