Fork me on GitHub

iOS开发 Swift 完成矩阵求逆

今天遇到一个需求,需要将一个矩阵(数组类型)进行求逆,查询资料发现之前某位大神的博文中有提到相关实现。原文链接传送门

文章中的方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Accelerate
func invert(_ matrix: [Double]) -> [Double] {
var inMatrix = matrix
var pivot : __CLPK_integer = 0
var workspace = 0.0
var error : __CLPK_integer = 0

var N = __CLPK_integer(sqrt(Double(matrix.count)))
dgetrf_(&N, &N, &inMatrix, &N, &pivot, &error)

if error != 0 {
return inMatrix
}

dgetri_(&N, &inMatrix, &N, &pivot, &workspace, &N, &error)
return inMatrix
}
var m = [1.0, 2.0, 3.0, 4.0]
invert(m) // returns [-2.0, 1.0, 1.5, -0.5]

 

文章中的 Demo 方案可以正常执行并正确输出,但是它并没有解决我的问题,因为这个方案有一个问题:当数组中元素个数超过 8 个,就会 Crash。

 

再次查询资料,发现了一份 OC 中的实现方案,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import <Accelerate/Accelerate.h>
static int matrix_invert(__CLPK_integer N, double *matrix) {
__CLPK_integer error = 0;
__CLPK_integer pivot_tmp[6 * 6];
__CLPK_integer *pivot = pivot_tmp;
double workspace_tmp[6 * 6];
double *workspace = workspace_tmp;
bool need_free = false;

if (N > 6) {
need_free = true;
pivot = malloc(N * N * sizeof(__CLPK_integer));
if (!pivot) return -1;
workspace = malloc(N * sizeof(double));
if (!workspace) {
free(pivot);
return -1;
}
}

dgetrf_(&N, &N, matrix, &N, pivot, &error);

if (error == 0) {
dgetri_(&N, matrix, &N, pivot, workspace, &N, &error);
}

if (need_free) {
free(pivot);
free(workspace);
}
return error;
}

 

其原理为检测到数组中元素个数超出预期的时候,预先分配出一块等量大小的内存空间。根据这个方案得出 Swift 中的解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import Accelerate

func matrix_invert(_ matrix: inout [Double]) -> Int {

// 这样写矩阵中总元素个数大于 8 的时候会发生越界导致 Crash
// var pivot : __CLPK_integer = 0
// var workspace = 0.0
// 这样写个数不受限制
let pivot = UnsafeMutablePointer<__CLPK_integer>.allocate(capacity: matrix.count)
let workspace = UnsafeMutablePointer<Double>.allocate(capacity: matrix.count)

var error : __CLPK_integer = 0

var n = __CLPK_integer(sqrt(Double(matrix.count)))
var m = n
var lda = n

dgetrf_(&m, &n, &matrix, &lda, pivot, &error)

if error != 0 {
return Int(error)
}

dgetri_(&m, &matrix, &lda, pivot, workspace, &n, &error)
return Int(error)
}

//var m = [1.0, 2.0, 3.0, 4.0] // returns [-2.0, 1.0, 1.5, -0.5]
var m = [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]
// returns [-1.0, -0.0, -0.0, -0.0, 1.0, 0.0, -1.0, -0.0, 1.0, 0.0, 0.0, 0.0, -0.0, -1.0, -0.0, -0.0, -0.0, 1.0, -0.0, -1.0, -0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 1.0, 0.0, 0.0, 0.0, 0.0]

matrix_invert(&m)

print(m)

 

修改之后,在两种测试用例下,都可以正常运行并输出 O(∩_∩)O~~

参考文献:

Swift Matrix and Machine Learning Library

------------- 本文结束感谢您的阅读 -------------