【发布时间】:2013-08-30 14:09:40
【问题描述】:
我正在尝试解决 Project Euler (http://projecteuler.net/problem=14) 的问题 14,但我使用 Haskell 遇到了死胡同。
现在,我知道这些数字可能足够小,我可以使用蛮力,但这不是我练习的目的。
我正在尝试将中间结果记住在Map Integer (Bool, Integer) 类型的Map 中,其含义是:
- the first Integer (the key) holds the number
- the Tuple (Bool, Interger) holds either (True, Length) or (False, Number)
where Length = length of the chain
Number = the number before him
例如:
for 13: the chain is 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
My map should contain :
13 - (True, 10)
40 - (False, 13)
20 - (False, 40)
10 - (False, 20)
5 - (False, 10)
16 - (False, 5)
8 - (False, 16)
4 - (False, 8)
2 - (False, 4)
1 - (False, 2)
现在,当我搜索另一个号码时,例如40,我知道链中有(10 - 1) length 等等。
我现在想,如果我搜索 10,不仅告诉我 10 的长度是 (10 - 3) length 并更新地图,而且我想更新 20、40 以防它们仍然是(False,_)
我的代码:
import Data.Map as Map
solve :: [Integer] -> Map Integer (Bool, Integer)
solve xs = solve' xs Map.empty
where
solve' :: [Integer] -> Map Integer (Bool, Integer) -> Map Integer (Bool, Integer)
solve' [] table = table
solve' (x:xs) table =
case Map.lookup x table of
Nothing -> countF x 1 (x:xs) table
Just (b, _) ->
case b of
True -> solve' xs table
False -> {-WRONG-} solve' xs table
f :: Integer -> Integer
f x
| x `mod` 2 == 0 = x `quot` 2
| otherwise = 3 * x + 1
countF :: Integer -> Integer -> [Integer] -> Map Integer (Bool, Integer) -> Map Integer (Bool, Integer)
countF n cnt (x:xs) table
| n == 1 = solve' xs (Map.insert x (True, cnt) table)
| otherwise = countF (f n) (cnt + 1) (x:xs) $ checkMap (f n) n table
checkMap :: Integer -> Integer -> Map Integer (Bool, Integer) -> Map Integer (Bool, Integer)
checkMap n rez table =
case Map.lookup n table of
Nothing -> Map.insert n (False, rez) table
Just _ -> table
在 {-WRONG-} 部分,我们应该更新所有值,如下例所示:
--We are looking for 10:
10 - (False, 20)
|
V {-finally-} update 10 => (True, 10 - 1 - 1 - 1)
20 - (False, 40) ^
| |
V update 20 => 20 - (True, 10 - 1 - 1)
40 - (False, 13) ^
| |
V update 40 => 40 - (True, 10 - 1)
13 - (True, 10) ^
| |
---------------------------
问题是我不知道它是否可以在一个函数中做两件事,比如更新一个数字并继续递归。在C 之类的语言中,我可能会执行类似(伪代码)的操作:
void f(int n, tuple(b,nr), int &length, table)
{
if(b == False) f (nr, (table lookup nr), 0, table);
// the bool is true so we got a length
else
{
length = nr;
return;
}
// Since this is a recurence it would work as a stack, producing the right output
table update(n, --cnt);
}
最后一条指令会起作用,因为我们通过引用发送 cnt。而且我们总是知道它会在某个时候完成,并且 cnt 不应该是
【问题讨论】:
-
映射的值类型会更惯用地呈现为
Either Integer Integer,但我认为您会发现对尚未记忆的条目没有映射会更方便。 -
如果你(也就是程序)知道需要更新什么,为什么不把更新后的表传给recurrence呢?
-
@groovy 因为我不知道在通话时(当我处于复发状态时)我应该更新什么值。在我知道答案之前,我首先必须重复多次,然后从下往上更新它们。看一下 10 的例子,我必须先到 20、40、13,然后我才知道要更新到 40、20、10。
标签: algorithm haskell implementation