【问题标题】:Improvement of matrix calculation in F#F#中矩阵计算的改进
【发布时间】:2012-01-29 15:28:37
【问题描述】:

我编写了一个代码来使用 F# 执行一些基本的矩阵计算。我想知道此代码是否有一些可能的改进,以减少计算时间。 实际上,执行的操作非常基本(主要是 2 个矩阵的乘法和转置),但是矩阵的大小很高(大约 10000 * 100000)导致计算时间很长(几个小时)。

我的问题/评论如下:

  1. 有什么方法可以改进下面的代码吗?有很多“为 循环”,这可能会导致算法严重变慢,但我 不知道如何避免这些“for 循环”。
  2. 我创建了一些初始值为 0 的初始矩阵,然后第二次用结果填充它们的元素。也许可以避免初始化的第一步。

这是算法:

// I use the #time function to calculate the calculation duration of the algorithm
#time

#r "Microsoft.Office.Interop.Excel"
#r "FSharp.PowerPack.dll"

open System
open System.IO

open Microsoft.FSharp.Math
open System.Collections.Generic

// Algorithm
let matrixCalculation (matA : matrix) (matB : matrix) (matC : matrix) =  

    // First step : Renamed the matrix A and B size to initialize the matrix "matrixCalcul" 
    let nbrOfElementsA = matA.NumRows
    let nbrOfElementsB = matB.NumRows
    let nbrOfCaracteristicsA = matA.NumCols
    let nbrOfCaracteristicsB = matB.NumCols

    // Second step : MatB has to be transposed 
    let tmatB = matB.Transpose

    // Initialisation of the final output named matrixCalcul. A weighted vector is also initialised 
    let mutable matrixCalcul = Matrix.create (nbrOfElementsA + 1) (nbrOfElementsB + 1) 0.            
    let mutable weightedVector = Matrix.create nbrOfCaracteristicsA 1 0.                   

    // The first column of matA and matB represents IDs, and are "copy/past" in matrixCalcul's first colum and first row respectively
    matrixCalcul.[1.. ,0..0] <- matA.[0..,0..0]
    matrixCalcul.[0..0,1 ..] <- matB.[0..,0..0].Transpose

    // Then the core of the matrix named "matrixCalcul" can be calculated
    for j = 0 to (nbrOfElementsB - 1) do
        weightedVector <- matC * tmatB.[1..(nbrOfCaracteristicsB - 1),0..(nbrOfElementsB-1)].Columns(j,1)                       
        for i = 0 to (nbrOfElementsA - 1) do
            let mutable acc  = matA.[0..(nbrOfElementsA - 1),1..(nbrOfCaracteristicsA-1)].Rows(i,1) * weightedVector                
            matrixCalcul.[i+1,j+1] <- (acc.[0,0])
    matrixCalcul


// Two matrix generators (one for matA and matB and another one for matC)

let matrixTestGeneratorAandB nbrOfElements nbrOfCaracteristics = 
    let matrixTestGeneratedAandB = Matrix.create nbrOfElements nbrOfCaracteristics 0.
                                   |> Matrix.mapi (fun i j value -> if j = 0 then float(i + 1) elif j % 2 = 0 then 1. else 0.)
    matrixTestGeneratedAandB

let matrixTestGeneratorC nbrOfElements nbrOfCaracteristics = 
    let matrixTestGeneratedC = Matrix.create nbrOfElements nbrOfCaracteristics 0.
                               |> Matrix.mapi (fun i j value -> if j = 0 then 0. elif j % 2 = 0 then 1. else 0.)
    matrixTestGeneratedC


// Generation of matrixA, matrixB and matrixC

let matrixA = matrixTestGeneratorAandB 100 179

let matrixB = matrixTestGeneratorAandB 100 639

let matrixC = matrixTestGeneratorC 178 638

// Calculation 
matrixCalculation matrixA matrixB matrixC

基本上计算持续时间约为 2 秒,但如果将 matrixAmatrixB 的数量更改为 10000,则可能需要一个小时。仅供参考,在我的算法中,matrixC 的大小将保持不变,只有矩阵 A 和 B 的行数可以增加。

如果您有任何改进的想法,我接受。

【问题讨论】:

  • 好的没问题我在上面
  • 您能否提供一个用于基准测试的测试脚本并更清楚地解释您的代码实际在做什么?
  • 我的直觉反应是从最内层循环中删除矩阵创建。
  • 我认为这个问题最好在 Code Review 上提出。
  • 您是否尝试过使用一些矩阵库来代替?

标签: f# matrix


【解决方案1】:

从您的代码中,很难理解您想要实现的目标。我认为您的意思是计算矩阵d[0..m, 0..n] 如下:

  +---------+-------------------------+
  | 0.0     | b00 b10 ......  b(n-1)0 |
  +---------+-------------------------+
  | a00     | d11 d12 ......  d1n     |
  | a10     | d21 d22 ......  d2n     |
  | ...     | ... ... ......  ...     |
  | ...     | ... ... ......  ...     |
  | ...     | ... ... ......  ...     |
  | a(m-1)0 | dm1 dm2 ......  dmn     |
  +---------+-------------------------+

其中核心部分(内部矩阵d[1..m, 1..n])是三个矩阵的乘法:matA1(修剪第一列后的matA)、matCmatB1(修剪第一列后的matB列和转置)。

要理解矩阵运算,一个很好的方法是推理矩阵大小。令racarbcbrccc 分别表示matAmatBmatC 中的行数和列数。乘法是在大小为ra x (ca-1)rc x cc(cb-1) x rb的三个矩阵之间进行的;这仅在 rc = ca-1cc = cb-1 时才有意义。我们得到了大小为(ra+1) x (rb+1) 的矩阵d

这是我不使用任何for 循环的尝试:

let calculate (matA : matrix) (matB : matrix) (matC : matrix) = 
    let ra = matA.NumRows
    let ca = matA.NumCols
    let rb = matB.NumRows
    let cb = matB.NumCols
    let matrixCalcul = Matrix.zero (ra+1) (rb+1)

    matrixCalcul.[1.., 0..0] <- matA.[0.., 0..0]
    matrixCalcul.[0..0, 1..] <- matB.[0.., 0..0].Transpose

    matrixCalcul.[1.., 1..] <- (matA.Columns(1, ca-1) * matC) * matB.Columns(1, cb-1).Transpose
    matrixCalcul

我已经分别测试了大小为 200x279、200x1279 和 278x1238 的 matAmatBmatC。两个版本产生相同的结果,我的功能比原来的快40x。这有很多原因,但一般来说,向量化版本在矩阵计算方面具有更好的性能。

【讨论】:

  • 只要努力就值得 +10!
  • 谢谢pad,它工作得很好。此外,我的问题不是很容易描述...再次感谢您
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-17
  • 2011-09-09
  • 2018-04-12
  • 1970-01-01
  • 2018-01-21
相关资源
最近更新 更多