在功能范式中实现Karger的最小割算法

时间:2014-05-29 11:23:13

标签: algorithm graph clojure functional-programming imperative-programming

我在任何命令式语言中实现此算法没有问题,但我正在努力在Clojure或任何其他函数式语言中实现它。在处理可变数据结构和命令性循环方面描述了很多算法,我很难将所有算法转换为功能域。

这是我在Clojure中使用邻接列表作为图形表示来实现它的不完整尝试(草稿,而不是工作实现):

(ns karger.core
  (:require [clojure.string :as string]))

(defn load-data []
    (zipmap 
     (range 1 1000)
     (map rest (map read-string
       (string/split (slurp "data.txt") #"\n")))))

(defn min-cut [graph] 
  (let [start (rand-int (count graph))
        end (rand-int (graph start))
        start-list (nth graph start)]
    (for [x (graph end)
          :when (not= x start)]
      (assoc graph start (conj start-list x)))
      ))

(count (load-data))

任何人都可以给我一个这个算法的参考实现(最好用Clojure编写)?另外,如果有人给我一个将命令性术语中描述的算法翻译成功能域的一般建议,我想。

提前致谢!

更新#1

以下是用Python编写的算法实现的链接:http://pastebin.com/WwWCtxpu

1 个答案:

答案 0 :(得分:1)

您的代码存在根本问题:

  • 您的start-list绑定是一个数字,不能conj为'
  • 您正在调用assoc并忽略返回值,从而使其成为无操作。
  • 您正在使用for,就好像它是一个循环结构(它是列表解析)
  • 您在哈希映射上调用nth,这将始终失败(zipmap返回哈希映射)

一般来说,函数式编程的想法是通过创建世界的#34;状态来将可变变量提升为不可变的局部绑定。完全由函数参数封装,并使用该状态的精炼版本进行函数调用。

这是一个基于您发布的python解决方案从头开始的工作实现,以及java示例here

使用的图形文件
(ns min-cut.core
  (:require [clojure.java.io :as io]
            [clojure.string :as string]
            [clojure.pprint :refer [pprint]]))

(defn make-maps
  [filename]
  (reduce (fn [graph line]
            (let [[node & edges] (->> line
                                      (#(string/split % #"\W+"))
                                      (remove #{""})
                                      (map read-string))]
              (assoc graph node (set edges))))
          {}
          (line-seq (io/reader filename))))

(defn karger
  [graph]
  (if (<= (count (keys graph))
          2)
    (count (graph (apply min (keys graph))))
    (let [start (rand-nth (keys graph))
          finish (rand-nth (vec (graph start)))
          graph (loop [g graph
                       [edge & edges] (seq (graph finish))]
                  (if-not edge
                    g
                    (recur
                     (if (= edge start)
                       g
                       (update-in g [start] conj edge))
                     edges)))
          graph (loop [g graph
                       [edge & edges] (seq (graph finish))]
                  (if-not edge
                    g
                    (let [gr (update-in g [edge] disj finish)
                          gr (if (= edge start)
                               gr
                               (update-in gr [edge] conj start))]
                      (recur gr edges))))
          graph (dissoc graph finish)]
      (recur graph))))

(defn -main
  [& [file]]
  (let [file (or file "kargerAdj.txt")
        graph (make-maps file)]
    (println "min cut is: "
             (reduce min (repeatedly 1801 #(karger graph))))))

这是python代码的非常直译,因此可以改进这些代码的多个地方。对于初学者来说,karger函数中的两个循环可能会被单个reduce替换,这将更加简洁明了。

请注意,此代码中创建的值不会发生变化 - 值会反弹,但不会更改任何传入的数据结构,并且使用的唯一全局定义是函数make-mapskarger-main - 所有数据都在本地绑定并传递给下一个用户。