矩阵 #

Math.js 支持多维矩阵和数组。矩阵可以被创建、操作和用于计算。常规的 JavaScript 数组和 math.js 实现的 matrix 类型可以在所有相关的 math.js 函数中互换使用。math.js 支持密集矩阵和稀疏矩阵。

数组和矩阵 #

Math.js 支持两种类型的矩阵

在大多数情况下,函数返回的矩阵类型由函数输入决定:输入为 `Array` 时返回 `Array`,输入为 `Matrix` 时返回 `Matrix`。当输入混合时,将返回 `Matrix`。对于输出类型无法从输入确定的函数,输出由配置选项 `matrix` 决定,该选项可以是一个字符串 `'Matrix'`(默认)或 `'Array'`。`size` 函数是一个例外:`size` 始终返回一个包含数字的 `Array`。在这种情况下,保持一致的输出类型是最实用的,因为大小经常在 JavaScript 循环中使用,而代码只能使用一个包含数字的扁平数组。这使得 `size` 函数与矩阵方法 `matrix.size()` 保持一致。

// create an array and a matrix
const array = [[2, 0], [-1, 3]]               // Array
const matrix = math.matrix([[7, 1], [-2, 3]]) // Matrix

// perform a calculation on an array and matrix
math.map(array, math.square)                  // Array,  [[4, 0], [1, 9]]
math.map(matrix, math.square)                 // Matrix, [[49, 1], [4, 9]]

// perform calculations with mixed array and matrix input
math.add(array, matrix)                       // Matrix, [[9, 1], [-3, 6]]
math.multiply(array, matrix)                  // Matrix, [[14, 2], [-13, 8]]

// create a matrix. Type of output of function ones is determined by the
// configuration option `matrix`
math.ones(2, 3)                               // Matrix, [[1, 1, 1], [1, 1, 1]]

创建 #

可以使用 `math.matrix` 函数从数组创建矩阵。提供的数组可以包含嵌套数组以创建多维矩阵。当不带参数调用时,将创建一个空矩阵。

// create matrices
math.matrix()                           // Matrix, size [0]
math.matrix([0, 1, 2])                  // Matrix, size [3]
math.matrix([[0, 1], [2, 3], [4, 5]])   // Matrix, size [3, 2]

Math.js 支持常规数组。可以通过嵌套数组来创建多个维度。

// create arrays
[]                                      // Array, size [0]
[0, 1, 2]                               // Array, size [3]
[[0, 1], [2, 3], [4, 5]]                // Array, size [3, 2]

矩阵可以包含不同类型的值:数字、复数、单位或字符串。不同的类型可以混合在同一个矩阵中。

// create a matrix with mixed types
const a = math.matrix([2.3, 'hello', math.complex(3, -4), math.unit('5.2 mm')]) 
a.subset(math.index(1))  // 'hello'

有许多函数可以创建具有特定大小和内容的矩阵:`ones`、`zeros`、`identity`。

// zeros creates a matrix filled with zeros
math.zeros(3)           // Matrix, size [3],    [0, 0, 0]
math.zeros(3, 2)        // Matrix, size [3, 2], [[0, 0], [0, 0], [0, 0]]
math.zeros(2, 2, 2)     // Matrix, size [2, 2, 2],
                        //   [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]

// ones creates a matrix filled with ones
math.ones(3)                        // Matrix, size [3],    [1, 1, 1]
math.multiply(math.ones(2, 2), 5)   // Matrix, size [2, 2], [[5, 5], [5, 5]]

// identity creates an identity matrix
math.identity(3)      // Matrix, size [3, 3], [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
math.identity(2, 3)   // Matrix, size [2, 3], [[1, 0, 0], [0, 1, 0]]

函数 `ones`、`zeros` 和 `identity` 也接受一个包含矩阵维度的单一数组或矩阵。当输入为数组时,函数将输出数组。当输入为矩阵时,输出将是矩阵。请注意,当参数为数字时,输出由第 数组和矩阵节中讨论的选项 `matrix` 决定。

// Array as input gives Array as output
math.ones([2, 3])               // Array,  size [3, 2], [[1, 1, 1], [1, 1, 1]]
math.ones(math.matrix([2, 3]))  // Matrix, size [3, 2], [[1, 1, 1], [1, 1, 1]]

可以使用 `range` 函数创建范围。`range` 函数使用参数 `start` 和 `end` 调用,还可以选择一个参数 `step`。范围的起始值包含在内,结束值不包含在内。

math.range(0, 4)        // [0, 1, 2, 3]
math.range(0, 8, 2)     // [0, 2, 4, 6]
math.range(3, -1, -1)   // [3, 2, 1, 0]

计算 #

math.js 的大多数函数都支持矩阵和数组。一元函数可以通过 `math.map(matrix, function)` 按元素应用。

// perform an element-wise operation on a matrix using math.map
const a = math.matrix([1, 4, 9, 16, 25])  // Matrix, [1, 4, 9, 16, 25]
math.map(a, math.sqrt)                    // Matrix, [1, 2, 3, 4, 5]

// use a function that has built-in matrix and array support
const b = [1, 2, 3, 4, 5] 
math.factorial(b)                         // Array,  [1, 2, 6, 24, 120]

// multiply an array with a matrix
const c = [[2, 0], [-1, 3]]               // Array
const d = math.matrix([[7, 1], [-2, 3]])  // Matrix
math.multiply(c, d)                       // Matrix, [[14, 2], [-13, 8]]

// add a number to a matrix (see broadcasting)
math.add(c, 2)                            // Array, [[4, 2], [1, 5]]

// calculate the determinant of a matrix
math.det(c)                               // 6
math.det(d)                               // 23

广播 #

需要两个或多个矩阵类型参数且按元素操作的函数,会自动按元素操作,就好像参数具有相同的大小一样。

A = math.matrix([1, 2])       // Matrix, [1, 2]
math.add(A, 3)                // Matrix, [3, 4]

B = math.matrix([[3], [4]])   // Matrix, [[3], [4]]
math.add(A, B)                // Matrix, [[4, 5], [5, 6]]

在一个参数中存在的任何索引,都可以视为存在于其他参数中,只要该维的大小为一或不存在。这在 N 维中是有效的。

在大小大于一的维度上不可能进行广播。

math.add([1, 2], [3, 4, 5])
// Error: shape missmatch: missmatch is found in arg with shape (2) not possible to broadcast dimension 0 with size 2 to size 3

math.add([[1], [2], [3]], [[4], [5]])
// Error: shape missmatch: missmatch is found in arg with shape (2,1) not possible to broadcast dimension 0 with size 2 to size 3

大小和维度 #

Math.js 使用几何维度

可以使用 `size` 函数计算矩阵的大小。`size` 函数返回一个 `Matrix` 或 `Array`,具体取决于配置选项 `matrix`。此外,矩阵本身也有一个 `size` 函数,它始终返回一个数组。

// get the size of a scalar
math.size(2.4)                                // Matrix, []
math.size(math.complex(3, 2))                 // Matrix, []
math.size(math.unit('5.3 mm'))                // Matrix, []

// get the size of a one-dimensional matrix (a vector) and a string
math.size([0, 1, 2, 3])                       // Array, [4]
math.size('hello world')                      // Matrix, [11]

// get the size of a two-dimensional matrix
const a = [[0, 1, 2, 3]]                      // Array
const b = math.matrix([[0, 1, 2], [3, 4, 5]]) // Matrix
math.size(a)                                  // Array, [1, 4]
math.size(b)                                  // Matrix, [2, 3]

// matrices have a function size (always returns an Array)
b.size()                                      // Array, [2, 3]

// get the size of a multi-dimensional matrix
const c = [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]
math.size(c)                                  // Array, [2, 2, 3]

请注意,维度本身没有附加含义。在创建和打印二维矩阵时,第一个维度通常显示为,第二个维度显示为。例如

console.table(math.zeros([2, 4]))
// 0 0 0 0
// 0 0 0 0

如果您有一个矩阵,其中第一个维度表示 `x`,第二个维度表示 `y`,这看起来会很令人困惑,因为 `x` 显示为(垂直),而 `y` 显示为(水平)。

重塑大小 #

可以使用矩阵的 `resize` 函数来重塑大小。该函数以一个包含新大小的数组作为第一个参数调用,并接受一个可选的默认值。默认情况下,新条目将被设置为 `0`,但可以传递一个不同的默认值,如 `null`,以清楚地表明条目尚未明确设置。

const a = math.matrix() // Matrix, size [0],       []
a.resize([2, 3])        // Matrix, size [2, 3],    [[0, 0, 0], [0, 0, 0]]
a.resize([2, 2, 2])     // Matrix, size [2, 2, 2],
                        //   [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]

const b = math.matrix()
b.resize([3], 7)        // Matrix, size [3],    [7, 7, 7]
b.resize([5], 9)        // Matrix, size [5],    [7, 7, 7, 9, 9]
b.resize([2])           // Matrix, size [2],    [7, 7]

可以使用 `squeeze` 函数压缩矩阵的外部维度。当使用 `subset` 获取或设置矩阵中的单个值时,该值也会被自动压缩或解压缩。

// squeeze a matrix
const a = [[[0, 1, 2]]]
math.squeeze(a)             // [0, 1, 2]
math.squeeze([[3]])         // 3

// when getting/setting a single value in a matrix using subset, 
// it automatically squeeze/unsqueeze the value
const b = math.matrix([[0, 1], [2, 3]])
b.subset(math.index(1, 0))  // 2 and not [[2]]

获取或替换子集 #

可以使用 `subset` 函数检索或替换矩阵的子集。矩阵有一个 `subset` 函数,它作用于矩阵本身:`Matrix.subset(index [, replacement])`。对于矩阵和数组,都可以使用静态函数 `subset(matrix, index [, replacement])`。当提供参数 `replacement` 时,函数将替换矩阵中的一个子集,如果未提供,则将返回矩阵的一个子集。

可以使用 `Index` 定义子集。`Index` 包含一个矩阵每个维度的单个值或一组值。可以使用 `index` 函数创建 `Index`。`subset` 返回结果的方式取决于您如何为每个维度指定索引

这意味着标量索引会消除维度,而数组、矩阵或范围索引会保留它们。有关此行为的更多详细信息和示例,请参阅 迁移到 v15 部分。

例如

const m = [
  [10, 11, 12],
  [20, 21, 22]
]

// Scalar index eliminates the dimension:
math.subset(m, math.index(1, 2))           // 22 (both dimensions indexed by scalars, result is a value)
math.subset(m, math.index(1, [2]))         // [22] (row dimension eliminated, column dimension preserved as array)
math.subset(m, math.index([1], 2))         // [22] (column dimension eliminated, row dimension preserved as array)
math.subset(m, math.index([1], [2]))       // [[22]] (both dimensions preserved as arrays)

math.config({legacySubset: true}) // switch to legacy behavior
math.subset(m, math.index(1, 2))           // 22
math.subset(m, math.index(1, [2]))         // 22
math.subset(m, math.index([1], 2))         // 22
math.subset(m, math.index([1], [2]))       // 22

math.js 中的矩阵索引是零基的,就像包括 JavaScript 本身在内的大多数编程语言一样。请注意,像 Matlab 和 Octave 这样的数学应用程序的工作方式不同,它们使用一基索引。

// create some matrices
const a = [0, 1, 2, 3]
const b = [[0, 1], [2, 3]]
const c = math.zeros(2, 2)
const d = math.matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
const e = math.matrix()

// get a subset
math.subset(a, math.index(1))                 // 1
math.subset(a, math.index([2, 3]))            // Array, [2, 3]
math.subset(a, math.index(math.range(0,4)))   // Array, [0, 1, 2, 3]
math.subset(b, math.index(1, 0))              // 2
math.subset(b, math.index(1, [0, 1]))         // Array, [2, 3]
math.subset(b, math.index([0, 1], [0]))       // Matrix, [[0], [2]]

// get a subset
d.subset(math.index([1, 2], [0, 1]))          // Matrix, [[3, 4], [6, 7]]
d.subset(math.index(1, 2))                    // 5

// replace a subset. The subset will be applied to a clone of the matrix
math.subset(b, math.index(1, 0), 9)           // Array, [[0, 1], [9, 3]]
math.subset(b, math.index(2, [0, 1]), [4, 5]) // Array, [[0, 1], [2, 3], [4, 5]]

// replace a subset. The subset will be applied to the matrix itself
c.subset(math.index(0, 1),1)                  // Matrix, [[0, 1], [0, 0]]
c.subset(math.index(1, [0, 1]), [2, 3])       // Matrix, [[0, 1], [2, 3]]
e.resize([2, 3], 0)                           // Matrix, [[0, 0, 0], [0, 0, 0]]
e.subset(math.index(1, 2), 5)                 // Matrix, [[0, 0, 0], [0, 0, 5]]

迁移到 mathjs v15 的索引行为 #

随着 math.js v15 的发布,subset 在索引矩阵和数组时的行为已发生更改。如果您的代码依赖于之前的行为(即使用大小为 1 的数组或矩阵进行索引总是会返回值本身),您可能需要更新您的代码或启用旧版模式。

要保持旧的索引行为而无需进行任何代码更改,请使用配置选项 `legacySubset`

math.config({ legacySubset: true })

要迁移您的代码,您需要将所有矩阵索引从旧的索引表示法更改为新的索引表示法。基本上:如果要将数组作为输出,则必须将标量索引包装在数组括号中。这里有一些例子

const m = math.matrix([[1, 2, 3], [4, 5, 6]])
v14 代码 v15 等效代码 结果
math.subset(m, math.index([0, 1], [1, 2])) 无需更改 [[2, 3], [5, 6]]
math.subset(m, math.index(1, [1, 2])) math.subset(m, math.index([1], [1, 2])) [[5, 6]]
math.subset(m, math.index([0, 1], 2)) math.subset(m, math.index([0, 1], [2])) [[3], [6]]
math.subset(m, math.index(1, 2)) 无需更改 6

提示
如果要获取标量值,请使用标量索引。
如果要保留维度,请使用数组、矩阵或范围索引。

获取和设置矩阵中的值 #

矩阵中有两种方法可以获取或设置矩阵中的单个值。需要注意的是,`set` 方法会修改矩阵。

const p = math.matrix([[1, 2], [3, 4]])
p.set([0, 1], 5)
// p is now [[1, 5], [3, 4]]
p.get([1, 0]) // 3

当使用 `.set()` 方法在当前矩阵大小之外的位置设置值时,矩阵将被重塑。默认情况下,新项将使用零初始化,但可以使用可选的第三个参数 `defaultValue` 指定替代值。

高级索引 #

布尔数组索引是一种技术,它允许您根据逻辑条件在数组中筛选、替换和设置值。这可以通过创建一个表示所需条件的布尔数组,然后使用该数组作为索引来选择满足这些条件的原始数组中的元素来实现。

例如,可以创建一个布尔数组来表示数组中的所有偶数,然后使用该数组来筛选原始数组,使其只包含偶数。或者,可以创建一个布尔数组来表示数组中大于某个值的所有元素,然后使用该数组来将原始数组中大于该值的所有元素替换为新值。

const q = [1, 2, 3, 4]
math.subset(q, math.index([true, false, true, false]))      // Array [1, 3]

// filtering
math.subset(q, math.index(math.larger(q, 2)))               // Array [3, 4]

// filtering with no matches
math.subset(q, math.index(math.larger(q, 5)))               // Array []

// setting specific values, please note that the replacement value is broadcasted
q = math.subset(q, math.index(math.smaller(q, 3)), 0)       // q = [0, 0, 3, 4]

// replacing specific values
math.subset(q, math.index(math.equal(q, 0)), [1, 2])        // q = [1, 2, 3, 4]

在解析器中,使用更简洁的方式可以实现相同的功能。请注意,`#` 之后的所有内容都是注释。

math.evaluate(`
q = [1, 2, 3, 4]
q[[true, false, true, false]] # Matrix [1, 3]
q[q>2]                        # Matrix [3, 4]
q[q>5]                        # Matrix []
q[q <3] = 0                   # q = [0, 0, 3, 4]
q[q==0] = [1, 2]              # q = [1, 2, 3, 4]
`)

索引中的表达式可以根据需要复杂,只要它能计算出一个与大小相同的布尔数组即可。

math.evaluate(`
q = [1, 2, 3, 4]
r = [6, 5, 4, 3]
q[q > 3 and r < 4]     # [4]
`)

迭代 #

矩阵包含 `map` 和 `forEach` 函数,用于迭代(多维)矩阵的所有元素。`map` 和 `forEach` 的回调函数有三个参数:`value`(当前迭代元素的 قيمة)、`index`(一个包含每个维度索引值的数组)和 `matrix`(正在迭代的矩阵)。此语法类似于原生 JavaScript 数组的 `map` 和 `forEach` 函数,区别在于索引不是数字而是包含每个维度数字的数组。

const a = math.matrix([[0, 1], [2, 3], [4, 5]])

// The iteration below will output the following in the console:
//    value: 0 index: [0, 0]
//    value: 1 index: [0, 1]
//    value: 2 index: [1, 0]
//    value: 3 index: [1, 1]
//    value: 4 index: [2, 0]
//    value: 5 index: [2, 1]
a.forEach(function (value, index, matrix) {
  console.log('value:', value, 'index:', index) 
}) 

// Apply a transformation on the matrix
const b = a.map(function (value, index, matrix) {
  return math.multiply(math.sin(value), math.exp(math.abs(value))) 
}) 
console.log(b.format(5))  // [[0, 2.2874], [6.7188, 2.8345], [-41.32, -142.32]]

// Create a matrix with the cumulative of all elements
let count = 0
const cum = a.map(function (value, index, matrix) {
  count += value 
  return count 
}) 
console.log(cum.toString())  // [[0, 1], [3, 6], [10, 15]]

迭代多个矩阵或数组 #

您可以通过使用 `map` 函数来迭代多个矩阵或数组。映射允许通过自动调整大小以相互匹配的方式对矩阵执行按元素操作。

要迭代多个矩阵,可以使用 `map` 函数。`map` 函数将给定函数应用于矩阵的每个元素,并返回一个包含结果的新矩阵。

这是一个迭代两个矩阵并添加它们对应元素的示例

const a = math.matrix([[1, 2], [3, 4]]);
const b = math.matrix([[5, 6], [7, 8]]);

const result = math.map(a, b, (x, y) => x + y);

console.log(result); // [[6, 8], [10, 12]]

在此示例中,`map` 函数将矩阵作为前两个参数,并将回调函数 `(x, y) => x + y` 作为第三个参数。回调函数应用于矩阵的每个元素,其中 `x` 表示来自矩阵 `a` 的相应元素,`y` 表示来自矩阵 `b` 的相应元素。结果是一个新矩阵,其中包含两个矩阵的按元素求和。

通过使用广播和 `map` 函数,您可以轻松地迭代多个矩阵并执行按元素操作。

const a = math.matrix([10, 20])
const b = math.matrix([[3, 4], [5, 6]])

const result = math.map(a, b, (x, y) => x + y)
console.log(result); // [[13, 24], [15, 26]]

还可以为回调提供索引和广播数组。例如 `(valueA, valueB, index)` 甚至 `(valueA, valueB, index, broadcastedMatrixA, broadcastedMatrixB)`。对于可以映射的矩阵数量 `N` 没有特定限制。因此,回调可以有 `N` 个参数,在包含索引的情况下有 `N+1` 个参数,或者在包含索引和广播矩阵的情况下有 `2N+1` 个参数。

目前 `forEach` 不包含相同的功能。

存储类型 #

Math.js 同时支持密集矩阵和稀疏矩阵。稀疏矩阵对于包含大量零的矩阵非常高效。在这种情况下,它们可以节省大量内存,并且计算速度可能比密集矩阵快得多。

Math.js 支持两种类型的矩阵

在创建矩阵时,可以使用构造函数 `matrix`、`diag`、`identity`、`ones` 和 `zeros` 来选择矩阵类型。

// create sparse matrices
const m1 = math.matrix([[0, 1], [0, 0]], 'sparse')
const m2 = math.identity(1000, 1000, 'sparse')

您还可以使用 `sparse` 函数将数组或矩阵强制转换为稀疏存储格式。

const md = math.matrix([[0, 1], [0,0]])  // dense
const ms = math.sparse(md)               // sparse

注意:对包含 *n* 个普通数字的 JavaScript 数组调用 `sparse` 会生成一个具有一列和 *n* 行的矩阵——与 `matrix` 不同,后者生成一个具有 *n* 个条目的 1 维矩阵对象,即一个向量(*不是* 1x*n* 的“行向量”也不是 *n*x1 的“列向量”,而只是一个长度为 *n* 的普通向量)。

const mv = math.matrix([0, 0, 1])  // Has size [3]
const mc = math.sparse([0, 0, 1])  // A "column vector," has size [3, 1]

API #

math.js 中所有相关的函数都支持矩阵和数组。像 `math.add`、`math.subtract`、`math.sqrt` 这样的函数按元素处理矩阵。有一组函数专门用于创建或操作矩阵,例如:

矩阵函数的完整列表可在 函数参考页面 上找到。

math.js 中提供两种类型的矩阵类,用于存储密集矩阵和稀疏矩阵。尽管它们包含如下文档所述的公共函数,但直接使用以下 API *不* 是推荐的做法。尽可能优先使用“math”命名空间中的函数。

Fork me on GitHub