Accelerate框架包含
LAPACK线性代数包,其中包括
DGELS函数,用于解决欠定或超定的线性系统。根据文档:
DGELS通过使用A的QR或LQ分解来解决涉及M×N矩阵A或其转置的过定或欠定实线性系统。假设A具有完全秩。
以下是如何从Swift中使用该函数的示例。这本质上是
此C示例代码的翻译。
func solveLeastSquare(A A: [[Double]], B: [Double]) -> [Double]? {
precondition(A.count == B.count, "Non-matching dimensions")
var mode = Int8(bitPattern: UInt8(ascii: "N"))
var nrows = CInt(A.count)
var ncols = CInt(A[0].count)
var nrhs = CInt(1)
var ldb = max(nrows, ncols)
var localA = (0 ..< nrows * ncols).map {
A[Int($0 % nrows)][Int($0 / nrows)]
}
var localB = B
if ldb > nrows {
localB.appendContentsOf([Double](count: ldb - nrows, repeatedValue: 0.0))
}
var wkopt = 0.0
var lwork: CInt = -1
var info: CInt = 0
dgels_(&mode, &nrows, &ncols, &nrhs, &localA, &nrows, &localB, &ldb, &wkopt, &lwork, &info)
lwork = Int32(wkopt)
var work = [Double](count: Int(lwork), repeatedValue: 0.0)
dgels_(&mode, &nrows, &ncols, &nrhs, &localA, &nrows, &localB, &ldb, &work, &lwork, &info)
if info != 0 {
print("A does not have full rank; the least squares solution could not be computed.")
return nil
}
return Array(localB.prefix(Int(ncols)))
}
一些注意事项:
dgels_()
修改传递的矩阵和向量数据,并期望矩阵作为“平坦”数组包含A
的列。同时,右侧的向量预期为具有长度max(M, N)
的数组。因此,首先将输入数据复制到本地变量。
- 所有参数都必须通过引用传递给
dgels_()
,这就是为什么它们都存储在var
中的原因。
- C整数是32位整数,这使得一些
Int
和CInt
之间的转换成为必要。
示例1: 过度确定的系统,来源于http://www.seas.ucla.edu/~vandenbe/103/lectures/ls.pdf。
let A = [[ 2.0, 0.0 ],
[ -1.0, 1.0 ],
[ 0.0, 2.0 ]]
let B = [ 1.0, 0.0, -1.0 ]
if let x = solveLeastSquare(A: A, B: B) {
print(x)
}
示例2:欠定系统,对于
x_1 + x_2 + x_3 = 1.0
的最小范数解。
let A = [[ 1.0, 1.0, 1.0 ]]
let B = [ 1.0 ]
if let x = solveLeastSquare(A: A, B: B) {
print(x)
}
更新至 Swift 3 和 Swift 4:
func solveLeastSquare(A: [[Double]], B: [Double]) -> [Double]? {
precondition(A.count == B.count, "Non-matching dimensions")
var mode = Int8(bitPattern: UInt8(ascii: "N"))
var nrows = CInt(A.count)
var ncols = CInt(A[0].count)
var nrhs = CInt(1)
var ldb = max(nrows, ncols)
var localA = (0 ..< nrows * ncols).map { (i) -> Double in
A[Int(i % nrows)][Int(i / nrows)]
}
var localB = B
if ldb > nrows {
localB.append(contentsOf: [Double](repeating: 0.0, count: Int(ldb - nrows)))
}
var wkopt = 0.0
var lwork: CInt = -1
var info: CInt = 0
var nrows_copy = nrows
dgels_(&mode, &nrows, &ncols, &nrhs, &localA, &nrows_copy, &localB, &ldb, &wkopt, &lwork, &info)
lwork = Int32(wkopt)
var work = [Double](repeating: 0.0, count: Int(lwork))
dgels_(&mode, &nrows, &ncols, &nrhs, &localA, &nrows_copy, &localB, &ldb, &work, &lwork, &info)
if info != 0 {
print("A does not have full rank; the least squares solution could not be computed.")
return nil
}
return Array(localB.prefix(Int(ncols)))
}
A * x = b
,其中A
的形状为(n, 2)
,x
的形状为(2,)
,b
的形状为(n,)
,则最小二乘解与等效的A.T * A * x = A.T * b
的解相同,由于A.T * A
的形状为(2, 2)
,而A.T * b
的形状为(2,)
,因此您有一个非常简单的系统,可以轻松解决,例如使用Cramer's rule。从头开始编写应该非常简单。 - Jaime