2010-11-06 35 views
8

Tôi quyết định làm việc thông qua văn bản Giới thiệu về thuật toán CLRS và chọn vấn đề in gọn gàng here.Clojure lập trình để giải quyết thuật toán lập trình động

Tôi đã khắc phục sự cố và đưa ra một giải pháp bắt buộc đơn giản để triển khai bằng Python, nhưng hơi kém trong Clojure.

Tôi hoàn toàn bối rối khi dịch hàm ma trận tính toán từ giải pháp của tôi thành Clojure thành ngữ. Bất kỳ đề xuất? Dưới đây là mã giả cho hàm tính toán ma trận:

// n is the dimension of the square matrix. 
// c is the matrix. 
function compute-matrix(c, n): 
    // Traverse through the left-lower triangular matrix and calculate values. 
    for i=2 to n: 
     for j=i to n: 

      // This is our minimum value sentinal. 
      // If we encounter a value lower than this, then we store the new 
      // lowest value. 
      optimal-cost = INF 

      // Index in previous column representing the row we want to point to. 
      // Whenever we update 't' with a new lowest value, we need to change 
      // 'row' to point to the row we're getting that value from. 
      row = 0 

      // This iterates through each entry in the previous column. 
      // Note: we have a lower triangular matrix, meaning data only 
      // exists in the left-lower half. 
      // We are on column 'i', but because we're in a left-lower triangular 
      // matrix, data doesn't start until row (i-1). 
      // 
      // Similarly, we go to (j-1) because we can't choose a configuration 
      // where the previous column ended on a word who's index is larger 
      // than the word index this column starts on - the case which occurs 
      // when we go for k=(i-1) to greater than (j-1) 
      for k=(i-1) to (j-1): 

       // When 'j' is equal to 'n', we are at the last cell and we 
       // don't care how much whitespace we have. Just take the total 
       // from the previous cell. 
       // Note: if 'j' < 'n', then compute normally. 

       if (j < n): 
        z = cost(k + 1, j) + c[i-1, k] 

       else: 
        z = c[i-1, k] 

       if z < optimal-cost: 
        row = k 
        optimal-cost = z 

      c[i,j] = optimal-cost 
      c[i,j].row = row 

Ngoài ra, tôi sẽ rất nhiều đánh giá cao phản hồi về phần còn lại của nguồn Clojure của tôi, đặc biệt là liên quan đến cách thành ngữ nó được. Tôi đã cố gắng suy nghĩ đầy đủ bên ngoài mô hình mệnh lệnh cho mã Clojure tôi đã viết cho đến nay chưa? Ở đây là:

(ns print-neatly) 

;----------------------------------------------------------------------------- 
; High-order function which returns a function that computes the cost 
; for i and j where i is the starting word index and j is the ending word 
; index for the word list "word-list." 
; 
(defn make-cost [word-list max-length] 
    (fn [i j] 
    (let [total (reduce + (map #(count %1) (subvec word-list i j))) 
      result (- max-length (+ (- j i) total))] 
     (if (< result 0) 
     nil 
     (* result result result))))) 

;----------------------------------------------------------------------------- 
; initialization function for nxn matrix 
; 
(defn matrix-construct [n cost-func] 
    (let [; Prepend nil to our collection. 
     append-empty 
      (fn [v] 
      (cons nil v)) 

     ; Like append-empty; append cost-func for first column. 
     append-cost 
      (fn [v, index] 
      (cons (cost-func 0 index) v)) 

     ; Define an internal helper which calls append-empty N times to create 
     ; a new vector consisting of N nil values. 
     ; ie., [nil[0] nil[1] nil[2] ... nil[N]] 
     construct-empty-vec 
      (fn [n] 
      (loop [cnt n coll()] 
       (if (neg? cnt) 
       (vec coll) 
       (recur (dec cnt) (append-empty coll))))) 

     ; Construct the base level where each entry is the basic cost function 
     ; calculated for the base level. (ie., starting and ending at the 
     ; same word) 
     construct-base 
      (fn [n] 
      (loop [cnt n coll()] 
       (if (neg? cnt) 
       (vec coll) 
       (recur (dec cnt) (append-cost coll cnt)))))] 

    ; The main matrix-construct logic, which just creates a new Nx1 vector 
    ; via construct-empty-vec, then prepends that to coll. 
    ; We end up with a vector of N entries where each entry is a Nx1 vector. 
    (loop [cnt n coll()] 
     (cond 
     (zero? cnt) (vec coll) 
     (= cnt 1) (recur (dec cnt) (cons (construct-base n) coll)) 
     :else (recur (dec cnt) (cons (construct-empty-vec n) coll)))))) 

;----------------------------------------------------------------------------- 
; Return the value at a given index in a matrix. 
; 
(defn matrix-lookup [matrix row col] 
    (nth (nth matrix row) col)) 

;----------------------------------------------------------------------------- 
; Return a new matrix M with M[row,col] = value 
; but otherwise M[i,j] = matrix[i,j] 
; 
(defn matrix-set [matrix row col value] 
    (let [my-row (nth matrix row) 
     my-cel (assoc my-row col value)] 
    (assoc matrix row my-cel))) 

;----------------------------------------------------------------------------- 
; Print the matrix out in a nicely formatted fashion. 
; 
(defn matrix-print [matrix] 
    (doseq [j (range (count matrix))] 
    (doseq [i (range (count matrix))] 
     (let [el (nth (nth matrix i) j)] 
     (print (format "%1$8.8s" el)))) ; 1st item max 8 and min 8 chars 
    (println))) 


;----------------------------------------------------------------------------- 
; Main 
;----------------------------------------------------------------------------- 


;----------------------------------------------------------------------------- 
; Grab all arguments from the command line. 
; 
(let [line-length (Integer. (first *command-line-args*)) 
     words (vec (rest *command-line-args*)) 
     cost (make-cost words line-length) 
     matrix (matrix-construct (count words) cost)] 
    (matrix-print matrix)) 

EDIT: Tôi đã cập nhật chức năng ma trận-xây dựng của tôi với các thông tin phản hồi nhất định, vì vậy bây giờ nó thực sự là một dòng ngắn hơn so với thực hiện Python của tôi.

;----------------------------------------------------------------------------- 
; Initialization function for nxn matrix 
; 
(defn matrix-construct [n cost-func] 
    (letfn [; Build an n-length vector of nil 
      (construct-empty-vec [n] 
      (vec (repeat n nil))) 

      ; Short-cut so we can use 'map' to apply the cost-func to each 
      ; element in a range. 
      (my-cost [j] 
      (cost-func 0 j)) 

      ; Construct the base level where each entry is the basic cost function 
      ; calculated for the base level. (ie., starting and ending at the 
      ; same word) 
      (construct-base-vec [n] 
      (vec (map my-cost (range n))))] 

    ; The main matrix-construct logic, which just creates a new Nx1 vector 
    ; via construct-empty-vec, then prepends that to coll. 
    ; We end up with a vector of N entries where each entry is a Nx1 vector. 
    (let [m (repeat (- n 1) (construct-empty-vec n))] 
     (vec (cons (construct-base-vec n) m))))) 

Trả lời

3
  1. Thay vì sử dụng hãy cho phép với fn trong đó, hãy thử letfn.
  2. liều lượng liều lượngq -> có vẻ như nó có khả năng sẽ tốt hơn để hiểu được
  3. Cond/zero của bạn?/= 1 mã sẽ dễ đọc hơn (và nhanh hơn) với trường hợp.
  4. spidey-Cảm giác của tôi nói với tôi rằng vòng/tái đây nên có một số loại cuộc gọi bản đồ thay vì
  5. tôi rất nghi ngờ rằng đây sẽ là xa nhanh hơn với mảng nguyên thủy (và có thể sạch hơn ở một số nơi)
  6. Bạn có thể muốn sử dụng hoặc xem nguồn cho Incanter
+0

1. Đề xuất tuyệt vời. Tôi không biết có một thứ như letfn. 2. Tôi đã cố gắng để hiểu được việc xây dựng ma trận sử dụng liều lượngq. Hy vọng rằng có nhiều đề xuất hơn cho các ví dụ thực tế? :) 4. Tôi đã sử dụng một số bản đồ, nhưng vết thương bằng cách sử dụng '_' để bỏ qua một số giá trị và nghĩ rằng đó là "không tinh khiết", vì thiếu một từ tốt hơn. Nó có thể là thành ngữ hơn, mặc dù, như tôi đã nhìn thấy nó trong một số ví dụ gần đây. 5. Bạn có ý nghĩa gì bởi 'mảng nguyên thủy'? Cảm ơn phản hồi! (câu trả lời được rút gọn để phù hợp với các ràng buộc về nhận xét) – GrooveStomp

2

Chức năng tra cứu ma trận và ma trận có thể được đơn giản hóa. Bạn có thể sử dụng assoc-inget-in để thao tác cấu trúc liên kết lồng nhau.

(defn matrix-lookup [matrix row col] 
(get-in matrix [row col])) 

(defn matrix-set [matrix row col value] 
    (assoc-in matrix [row col] value)) 

Alex Miller đã đề cập sử dụng mảng nguyên thủy. Nếu bạn cần phải đi theo hướng đó, bạn có thể bắt đầu bằng cách xem int-array, aset-intaget. Xem tài liệu clojure.core để tìm hiểu thêm.

+0

Tôi thích điều này. get-in là thực sự đơn giản hơn so với ma trận tra cứu của tôi, vì vậy tôi chỉ nuked chức năng đó và rút ngắn codebase của tôi. – GrooveStomp

+0

Tôi cũng sẽ xem xét các mảng nguyên thủy. Cảm ơn bạn về thông tin! – GrooveStomp

2

Tôi leo lên tường và có thể nghĩ theo cách giống như Clojure để dịch thuật toán ma trận tính toán lõi thành một chương trình khả thi.

Nó chỉ dài hơn một dòng so với triển khai Python của tôi, mặc dù nó có vẻ được viết nhiều hơn. Để chắc chắn, các khái niệm như 'bản đồ' và 'giảm' là các hàm cấp cao hơn yêu cầu bạn đặt giới hạn suy nghĩ của mình.

Tôi tin rằng việc triển khai này cũng khắc phục lỗi trong Python của tôi. :)

;----------------------------------------------------------------------------- 
; Compute all table entries so we can compute the optimal cost path and 
; reconstruct an optimal solution. 
; 
(defn compute-matrix [m cost] 
    (letfn [; Return a function that computes 'cost(k+1,j) + c[i-1,k]' 
      ; OR just 'c[i-1,k]' if we're on the last row. 
      (make-min-func [matrix i j] 
      (if (< j (- (count matrix) 1)) 
       (fn [k] 
       (+ (cost (+ k 1) j) (get-in matrix [(- i 1) k]))) 
       (fn [k] 
       (get-in matrix [(- i 1) k])))) 

      ; Find the minimum cost for the new cost: 'cost(k+1,j)' 
      ; added to the previous entry's cost: 'c[i-1,k]' 
      (min-cost [matrix i j] 
      (let [this-cost (make-min-func matrix i j) 
        rang (range (- i 1) (- j 1)) 
        cnt (if (= rang()) (list (- i 1)) rang)] 
       (apply min (map this-cost cnt)))) 

      ; Takes a matrix and indices, returns an updated matrix. 
      (combine [matrix indices] 
      (let [i (first indices) 
        j (nth indices 1) 
        opt (min-cost matrix i j)] 
       (assoc-in matrix [i j] opt)))] 

    (reduce combine m 
     (for [i (range 1 (count m)) j (range i (count m))] [i j])))) 

Cảm ơn Alex và Jake đã bình luận của bạn. Cả hai đều rất hữu ích và đã giúp tôi trên đường đến Clojure thành ngữ.

1

Cách tiếp cận chung của tôi đối với các chương trình động trong Clojure không gây rối với việc xây dựng ma trận các giá trị trực tiếp, mà là sử dụng ghi nhớ song song với bộ kết hợp điểm cố định. Đây là ví dụ của tôi về khoảng cách chỉnh sửa máy tính:

(defn edit-distance-fp 
    "Computes the edit distance between two collections" 
    [fp coll1 coll2] 
    (cond 
     (and (empty? coll1) (empty? coll2)) 0 
     (empty? coll2) (count coll1) 
     (empty? coll1) (count coll2) 
     :else (let [x1 (first coll1) 
        xs (rest coll1) 
        y1 (first coll2) 
        ys (rest coll2)] 
       (min 
        (+ (fp fp xs ys) (if (= x1 y1) 0 1)) 
        (inc (fp fp coll1 ys)) 
        (inc (fp fp xs coll2)))))) 

Sự khác biệt duy nhất từ ​​giải pháp đệ quy ngây thơ ở đây chỉ đơn giản là thay thế các cuộc gọi đệ quy bằng lệnh gọi tới fp.

Và sau đó tôi tạo một điểm cố định memoized với:

(defn memoize-recursive [f] (let [g (memoize f)] (partial g g))) 

(defn mk-edit-distance [] (memoize-recursive edit-distance-fp)) 

Và sau đó gọi nó với:

> (time ((mk-edit-distance) 
    "the quick brown fox jumped over the tawdry moon" 
    "quickly brown foxes moonjumped the tawdriness")) 
"Elapsed time: 45.758 msecs" 
23 

tôi thấy memoization dễ dàng hơn để quấn bộ não của tôi xung quanh hơn đột biến bảng.

Các vấn đề liên quan