【问题标题】:Matrix multiplication in scheme, List of lists方案中的矩阵乘法,列表列表
【发布时间】:2019-02-10 12:54:52
【问题描述】:

我开始学习Scheme,有些不懂。我正在使用 DrRacket。

我写了以下代码:

(define mult_mat
  (λ (A B)
    (Trans_Mat (map (λ (x) (mul_Mat_vec A x))
                    (Trans_Mat B)))))

使用这个函数:

(define Trans_Mat
  (λ (A)
    (apply map (cons list A))))

(define mul_Mat_vec
  (λ (A v)
    (map (λ (x) (apply + (map * x v)))
         A)))

mult_mat 中,我在转置矩阵 B 的每个向量中乘以矩阵 A。 它工作正常。

我在网上找到了一个代码,它以我不理解的方式进行乘法运算:

(define (matrix-multiply matrix1 matrix2)
  (map
   (λ (row)
     (apply map
       (λ column
         (apply + (map * row column)))
       matrix2))
   matrix1))

在这段代码中,row 是矩阵 A 的列表,但我不明白 column 是如何更新的。

这部分代码:(apply + (map * row column))是向量row和向量column的点积

例如:A 是一个 2X3 矩阵,B 是一个 3X2 矩阵,如果我写的是 1 而不是 (apply + (map * row column)),那么我将得到一个 2X2 矩阵,其条目值为 1

我不明白它是如何工作的。

谢谢。

【问题讨论】:

  • 如果代码有效,你的问题可能在这里跑题了。

标签: matrix scheme racket apply transpose


【解决方案1】:

啊,老 ( apply map foo _a_list_ ) 把戏。非常聪明。

实际上(apply map (cons list A))(apply map list A) 是一样的。这就是 apply 的定义方式。

尝试一些具体的例子通常有助于“理解”:

(apply map       list '((1 2 3)  (10 20 30)) )
=
(apply map (cons list '((1 2 3)  (10 20 30))))
=
(apply map (list list  '(1 2 3) '(10 20 30) ))
=
(      map       list  '(1 2 3) '(10 20 30)  )
=
'((1 10) (2 20) (3 30))

这样最后一个参数'((1 2 3) (10 20 30)) 的元素被拼接到整个apply map ... 形式中。

矩阵转置(确实是列表的列表)。

所以你有

(define (mult_mat A B)
    (Trans_Mat (map (λ (B_column) (mul_Mat_vec A B_column))
                    (Trans_Mat B))))

(define (Trans_Mat A)
    (apply map list A))

(define (mul_Mat_vec A v)
    (map (λ (A_row) (apply + (map * A_row v)))
         A))

(define (matrix-multiply A B)
  (map
    (λ (A_row)
      (apply map
             (λ B_column
               (apply + (map * A_row B_column)))
             B))
    A))

注意它是(λ B_column ...,没有括号。在((λ args ...) x y z) 中,当输入 lambda 时,args 将所有参数打包到一个列表中:

((λ args ...) x y z)
=
(let ([args (list x y z)])
  ...)

另请注意

      (apply map
             (λ B_column
               (apply + (map * A_row B_column)))
             B)

遵循相同的“棘手”模式。其实是一样的

      (apply map (cons
             (λ B_column
               (apply + (map * A_row B_column)))
             B    ) )
=
      (      map
             (λ B_column
                (apply + (map * A_row B_column)))
             B_row1
             B_row2
             ....
             B_rowN )
=
     (cons (let ([B_column_1 (map car B)])
              (apply + (map * A_row B_column_1)))
           (map (λ B_column
                    (apply + (map * A_row B_column)))
             (cdr B_row1)
             (cdr B_row2)
             ....
             (cdr B_rowN)) )
=
     (cons 
       (apply (λ B_column (apply + (map * A_row B_column)))
              (map car B))
       (apply map
              (λ B_column
                 (apply + (map * A_row B_column)))
              (map cdr B)))

根据map的定义。

因此,通过应用map,矩阵被“打开”到其元素列表中的行,然后当多参数map开始处理这些rows 作为其参数,lambda 函数相应地一致地应用于每一行的后续数字;从而达到与 显式转置 相同的效果。但现在额外的好处是,我们不需要像第一个版本那样将结果转回正确的形式。

非常聪明,很好。


因此,有了所有这些理解,让我们尝试重新阅读原始代码,看看我们是否也能看到它。

(define (matrix-multiply matrix1 matrix2)
  (map
   (λ (row)
     (apply map
       (λ column      ;; <<------ no parens!
         (apply + (map * row column)))
       matrix2))
   matrix1))

这读作:对于matrix1 中的每个row,多参数map 一个lambda 超过matrix2matrix2 本身也是一个行列表;当我们在行上使用 multi-arg-map 时,lambda 会依次应用于行中的每一列。

因此,对于matrix1 中的每个,对于matrix2 中的每个,将该行和该列逐元素相乘,然后将结果相加;从而将每一行转换为这些总和的列表。这显然只有在行的长度和每列的长度相同时才有效:如果第一个矩阵的“宽度”和第二个矩阵的“高度”相同。

【讨论】:

  • 嗨,有内置的库可以做到这一点吗?我看过(require math/array),但我认为它不支持常见的运算:乘法、逆运算等。我可以自己重新实现它,但这只是我的应用程序的一小部分,我想采取“伟大的艺术家偷窃”铭记于心。我已经看到 CL 支持这个 (reddit.com/r/lisp/comments/6b21ka/…),当我使用它时,numpy 已经证明它非常擅长这个,但我想在 Racket 中做到这一点。谢谢!
  • 对不起,不知道。 Racket 有可搜索的文档。
  • (需要数学/矩阵)是用于乘法等的内置库docs.racket-lang.org/math/matrices.html?q=math
【解决方案2】:

如果您更喜欢使用 while 循环(这对初学者来说可能更容易),我建议将问题拆分为 7 个主要辅助函数(以及其他一些简单函数):

这不是最有效的方法(到目前为止),但很容易理解

  1. getRow mat i:获取矩阵mat的第i行(列表列表)

     (define (getRow mat i)
        (nthElement mat i))
    
    (define (nthElement lisT n)
        (if (= n 0)
            (car lisT)                                                 
            (nthElement (cdr lisT) (- n 1))))
    
  2. getCol mat i:获取矩阵mat的第i列(列表列表)

    (define (getCol mat i)
        (define col (list))
        (define row 0)
        (while (< row (length mat))
            (set! col (append col (list (valueAtIJ mat row i))))
            (set! row (+ row 1)))
         col)
    
    (define (valueAtIJ mat i j)
        (nthElement (nthElement mat i) j))
    
  3. listMult list1 list2:对两个列表执行逐元素乘法

    (define (listMult list1 list2)
        (if (not (null? list1))
            (cons (* (car list1) (car list2)) (listMult (cdr list1) (cdr list2)))
            null))
    
  4. sum aList:计算列表中所有元素的总和

    (define (sum aList)
        (if (null? aList)
            0
            (+ (car aList) (sum (cdr aList)))))
    
  5. length aList:查找列表的长度

    (define (length lisT)
        (if (null? lisT)                                               
            0
            (+ 1 (length (cdr lisT)))))
    
  6. newMatrix m n val:创建一个用 val 填充的 m × n 矩阵

    (define (newMatrix m n val)                        
        (define i 0)
        (define row (list val))
        (define mat (list))
        (if (= n 0)
            (list)                                          
            (begin 
                (while (< i (- n 1))
                    (set! row (append row (list val))) 
                    (set! i (+ i 1)))
                (set! i 0)
                (while (< i m)
                    (set! mat (append mat (list row)))     
                    (set! i (+ i 1)))
        mat)))
    
  7. setValueAtIJ mat i j val:设置mat中i,j位置的值val(从0开始)

    (define (setValueAtIJ mat i j val)
        (set! mat (setNthElementFinal mat i (setNthElementFinal (nthElement mat i) j val)))
        mat) 
    
    

这些都可以组合成矩阵乘法函数

(define (matrixMult mat1 mat2)
    (define mat1Dim (list (length mat1) (length (nthElement mat1 0))))      
    (define mat2Dim (list (length mat2) (length (nthElement mat2 0))))      
    (define i 0)
    (define j 0)
    (define newMat (newMatrix (car mat1Dim) (car (cdr mat2Dim)) 0))        
    (if (not (= (car (cdr mat1Dim)) (car mat2Dim)))
        null                                                                
        (begin
            (while (< i (length newMat))
                (while (< j (length (nthElement newMat 0)))
                    (set! newMat (setValueAtIJ newMat i j (sum (listMult (getRow mat1 i) (getCol mat2 j)))))
                    (set! j (+ j 1)))
                (set! j 0)
                (set! i (+ i 1)))
        newMat)))

【讨论】:

  • 用C写C代码,用Scheme写Scheme代码不是更好吗?
  • 我也是 Scheme 的初学者。只是试图提供另一个视角。这也是我在 Stack Overflow 上的第一篇文章。
【解决方案3】:

这个解决方案可能不是最好的写法,但很容易理解:

(define (matrixMultiply matrix1 matrix2)
    (define matrix2Transpose (matrixTranspose matrix2) ) ; Calculate matrix2 transpose to prevent recalculation in future
    (map 
        (lambda (row) ; Step1. Iterate through matrix1 rows
            (map
                (lambda (column) ; Step3. Iterate through matrix2 columns
                    (apply + (map * row column)) ; Step4. Multiply rows and columns by peer to peer and add them
                    ; Example: 
                    ; If row be (1 2) and column be (5 7) then:
                    ; Map part does: ((1 * 5) (2 * 7)) -> (5 14)
                    ; Apply part does: 5 + 14 -> 19
                )
                matrix2Transpose ; Step2. Use matrix2 transpose to get columns for every iteration
            )
        )
        matrix1
    )
)

(define (matrixTranspose matrix)
    (apply map (lambda _ _) matrix)
)

(display 
    (matrixMultiply '((1 2) (3 4)) '((5 6) (7 8)) )
)

输出:((19 22) (43 50))

【讨论】:

    猜你喜欢
    • 2018-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-20
    相关资源
    最近更新 更多