这不是一个完整的答案,但这里是来自维基百科文章的示例网络的可能编码。每个节点都有一个名称、一个后继(子)列表和一个概率表:
(defn node [name children fn]
{:name name :children children :table fn})
此外,这里有一些用于构建真/假概率的小辅助函数:
;; builds a true/false probability map
(defn tf [true-prob] #(if % true-prob (- 1.0 true-prob)))
上面的函数返回一个闭包,当给定一个true
值(resp. false
value)时,它返回事件的概率X=true
(对于X
我们正在编码的概率变量)。
由于网络是一个 DAG,我们可以直接相互引用节点(就像您提到的指针一样),而不必关心循环引用。我们只是按照拓扑顺序构建图:
(let [gw (node "grass wet" [] (fn [& {:keys [sprinkler rain]}]
(tf (cond (and sprinkler rain) 0.99
sprinkler 0.9
rain 0.8
:else 0.0))))
sk (node "sprinkler" [gw]
(fn [& {:keys [rain]}] (tf (if rain 0.01 0.4))))
rn (node "rain" [sk gw]
(constantly (tf 0.2)))]
(def dag {:nodes {:grass-wet gw :sprinkler sk :rain rn}
:joint (fn [g s r]
(*
(((:table gw) :sprinkler s :rain r) g)
(((:table sk) :rain r) s)
(((:table rn)) r)))}))
每个节点的概率表作为父节点状态的函数给出,并返回概率true
和false
值。例如,
((:table (:grass-wet dag)) :sprinkler true :rain false)
...返回{:true 0.9, :false 0.09999999999999998}
。
得到的联合函数根据以下公式组合概率:
P(G,S,R) = P(G|S,R).P(S|R).P(R)
并((:joint dag) true true true)
返回 0.0019800000000000004。实际上,由 所返回的每个值((:table <x>) <args>)
都是围绕 a 的闭包if
,它返回知道概率变量状态的概率。true
我们用各自的/值调用每个闭包false
以提取适当的概率,并将它们相乘。
在这里,我有点作弊,因为我认为应该通过遍历图形来计算联合函数(在一般情况下,宏可能会有所帮助)。这也让人感觉有点混乱,尤其是关于节点的状态,这些状态不一定只有真假:在一般情况下,您很可能会使用地图。