[英]Improvement of matrix calculation in F#
我编写了一个代码来使用F#执行一些基本的矩阵计算。 我想知道这段代码是否有一些可能的改进,以减少计算时间。 实际上,所执行的操作是非常基本的(主要是2个矩阵的乘法和转置),但矩阵的大小很高(大约10000 * 100000
),导致巨大的计算持续时间(几个小时)。
我的问题/评论如下:
这是算法:
// 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秒,但如果将matrixA
和matrixB
的数量更改为10000
,则可能需要1小时。 仅供参考,在我的算法中, matrixC
的大小将保持不变,只有矩阵A和B可以有越来越多的行。
如果你有任何改进的想法,我会接受它。
从您的代码中,很难理解您要实现的目标。 我认为你的意思是计算矩阵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
和matB1
( matB
修剪之后的第一列和转置) 。
要理解矩阵运算,一个好的方法是对矩阵大小进行推理。 令ra
, ca
, rb
, cb
, rc
和cc
分别表示matA
, matB
和matC
的行数和列数。 乘法是三个矩阵之间的大小ra x (ca-1)
, rc x cc
和(cb-1) x rb
; 这只有在rc = ca-1
和cc = 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
我已用测试matA
, matB
和matC
大小200x279,200x1279和278x1238的分别。 两个版本产生相同的结果,我的功能比原始版本快40x
倍。 这有很多原因,但一般来说,矢量化版本在矩阵计算方面具有更好的性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.