我编写了以下代码来解决Project Euler的No. 14:
为正整数集定义了以下迭代(Collatz)序列:
n → n/2 (n is even)
n → 3n + 1 (n is odd)
问:哪个起始编号低于一百万,产生最长链?
我的代码:
collatz :: Integer -> [Integer]
collatz 1 = [1]
collatz n =
filter (< 1000000) prev >>= poss
where prev = collatz (n - 1)
poss :: Integer -> [Integer]
poss prev
| even prev && prev `mod` 3 == 1 && (prev - 1) `div` 3 > 1 = [2 * prev, (prev - 1) `div` 3]
| otherwise = [2 * prev]
其中collatz n
返回将生成长度为n的Collatz链的数字列表。问题是,我只能不限制结果或限制整个链,而不仅仅是种子数,要低于1000,000。是否可以使用此模型来解决问题?
答案 0 :(得分:3)
我认为这种方法 - 虽然有趣 - 从根本上注定要失败。假设我发现导致长度为500的链的所有种子都超过2,000,000。我怎么知道我不会发现在三个以上的步骤中有一个低于1,000,000的种子让我在那里?我看不出你什么时候知道的。
我看到这个问题唯一可行的方法是计算从1到999,999的每个数字的collatz长度,然后做类似的事情:
main :: IO ()
main = do
let collatzMax = maximumBy (compare `on` collatzLength) [1..999999]
print collatzMax
另一方面,这提供了一个了解CAFs的绝佳机会,因为函数collatzLength
可以被天真地定义为:
collatzLength 1 = 1
collatzLength n | n `mod` 2 == 0 = 1 + collatzLength (n `div` 2)
collatzLength n = 1 + collatzLength (3 * n + 1)
这种递归让CAF感到尖叫。
当然,有备忘录模块可以为你建立CAF,但是自己构建一个是一个很有用的练习。这是懒惰的无限递归数据结构中的一个小小的迷你课程。
如果这让你失望,你可以浏览this spoiler如何使用CAF,然后使用不同的数据结构重写它。 (那么10路树而不是二叉树怎么样?怎么样以不同的顺序遍历树?你可以删除对showIntAtBase
的调用吗?)
答案 1 :(得分:0)
你的想法很有意思,虽然不是最有效的。它可能值得尝试,虽然它可能是内存密集型的。一些想法:
collatz
中过滤掉少量链条。你需要保留每次通过的所有数字。以这种方式调用collatz
是低效的,因为它会再次计算集合。使其成为共享值的无限列表会更有效:
collatz :: [[Integer]]
collatz = [1] : map (>>= poss) collatz
你需要弄明白你何时完成。为此,您需要浏览collatz
生成的数字列表,并计算其中有多少数量低于1000000.当您看到所有数字低于限制时,最后一个列表将包含数字最长的链条。
那就是说,我担心这种方法在计算上是不可行的。特别是,您将生成指数级数和指数级大数。例如,如果最长链为500,则该步骤中collatz
的结果将包含最多2 ^ 500的数字。如上所述,没有办法分辨哪些大数字可能是导致解决方案的那个,所以你不能放弃它们。