F#矩阵计算的改进

时间:2011-12-19 18:08:19

标签: f# matrix

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

我的问题/评论如下:

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

    // 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可以有越来越多的行。

    如果你有任何改进的想法,我会接受它。

1 个答案:

答案 0 :(得分:9)

从您的代码中,很难理解您要实现的目标。我认为您的意思是按如下方式计算矩阵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),matC和{{1修剪第一列并转置后的matB1

要理解矩阵运算,一个好的方法就是对矩阵大小进行推理。让matBracarbcbrc表示cc中的行数和列数,分别为matAmatB。乘法是三个大小为matCra x (ca-1)rc x cc的矩阵之间的乘法;这只有在(cb-1) x rbrc = ca-1时才有意义。我们得到了大小为cc = cb-1的结果矩阵d

这是我尝试不使用任何(ra+1) x (rb+1)循环:

for

我已经使用尺寸为200x279,200x1279和278x1238的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 matAmatB进行了测试。两个版本产生相同的结果,我的函数比原始版本快matC。这有很多原因,但一般来说,矢量化版本在矩阵计算方面具有更好的性能。