线性代数
vector 总和
这个ndarray箱子支持多种创建数组的方法 —— 该食谱主要专注std::Vec
的ndarray::Array
的创建,通过from_vec
完成。把两个数组加在一起和把两个数字加在一起,没有什么不同。在数组上,使用&
符号,能在一次算术运算中,阻止消耗数组的操作。没有&
的话,数组会被消耗。
在第一个示例中,数组a
和b
在 let 语句(z = a + b
)中移动:。 在第二个示例中,数组c
和d
不会移动,而是为w
创建一个新的数组。在 vector 总和(w = &c + &d
)之后,更新c
或d
,对w
值是没有影响的。另外,在打印c
时按预期工作,打印b
时出错,因为移动了。见两个数组的二元运算符更多细节。
extern crate ndarray; use ndarray::Array; fn main() { let a = Array::from_vec(vec![1., 2., 3., 4., 5.]); let b = Array::from_vec(vec![5., 4., 3., 2., 1.]); let mut c = Array::from_vec(vec![1., 2., 3., 4., 5.]); let mut d = Array::from_vec(vec![5., 4., 3., 2., 1.]); let z = a + b; let w = &c + &d; let epsilon = 1e-8; for elem in z.iter() { let diff: f32 = *elem - 6.; assert!(diff.abs() < epsilon); } println!("c = {}", c); c[0] = 10.; d[1] = 10.; for elem in w.iter() { let diff: f32 = *elem - 6.; assert!(diff.abs() < epsilon); } }
vector 范数
这个食谱演示了Array1
类型,ArrayView1
类型,fold
方法,以及dot
方法,它计算给定 vector 的l1和l2范数。l2 范数的计算是两者中,比较简单的一个,因为它是一个 vector 的点乘积的平方根,如l2_norm
函数所示。而 l1 范数,在l1_norm
函数,由fold
方法完成,它是元素绝对值的总和。(这也可以用x.mapv(f64::abs).scalar_sum()
执行,但这将为mapv
的结果分配一个新的数组。)
注意l1_norm
和l2_norm
都拿了ArrayView1
类型。这个食谱考虑了 vector 范数,因此范数函数,只需要接受一维 view(就是ArrayView1
)。当这个函数换成接受一个&Array1<f64>
类型参数,这将要求调用者具有,所有权数组的一个引用,这比仅对一个 view 的访问,更为严格(因为一个 view,是可以通过任何 array 或 view 创建的,而不仅仅是所有权数组)。对调用者来说,最方便的参数类型是&ArrayBase<S, Ix1> where S: Data
,因为调用者可以使用&array
或&view
,而不是x.view()
。 如果该函数是你公共 API 的一部分,那么对于用户来说,这可能是一个更好的选择,但是对于内部函数来说,简洁的ArrayView1<f64>
可能更好。
#[macro_use(array)] extern crate ndarray; use ndarray::{Array1, ArrayView1}; fn l1_norm(x: ArrayView1<f64>) -> f64 { x.fold(0., |acc, elem| acc + elem.abs()) } fn l2_norm(x: ArrayView1<f64>) -> f64 { x.dot(&x).sqrt() } fn normalize(mut x: Array1<f64>) -> Array1<f64> { let norm = l2_norm(x.view()); x.mapv_inplace(|e| e/norm); x } fn main() { let x = array![1., 2., 3., 4., 5.]; println!("||x||_2 = {}", l2_norm(x.view())); println!("||x||_1 = {}", l1_norm(x.view())); println!("Normalizing x yields {:?}", normalize(x)); }
矩阵相加
ndarray::arr2
创建两维矩阵,并把它们加在一起。
extern crate ndarray; use ndarray::arr2; fn main() { let a = arr2(&[[1, 2, 3], [4, 5, 6]]); let b = arr2(&[[6, 5, 4], [3, 2, 1]]); println!("Sum: {}", a + b); }
矩阵乘法
ndarray::arr2
创建两个矩阵,并用ndarray::ArrayBase::dot
对它们执行矩阵乘法。
extern crate ndarray; use ndarray::arr2; fn main() { let a = arr2(&[[1, 2, 3], [4, 5, 6]]); let b = arr2(&[[6, 3], [5, 2], [4, 1]]); println!("{}", a.dot(&b)); }
用 vector 和 矩阵,乘一个标量
用ndarray::arr1
创建一个,一维数组(vector),二维数组(矩阵)就用ndarray::arr2
。 首先,将一个标量乘以 vector ,得到另一个 vector。然后,矩阵用ndarray::Array2::dot
,乘以这个新的 vector。 (dot
会执行矩阵乘法,而*
运算符scalar * vector
,执行逐个元素的相乘。)在ndarray
中,一维数组根据上下文,可以解释为行或列 vector。如果 vector 表示的方向很重要,则要用二维数组的一行或一列。在本例中,vector 是右手边的一维数组,因此,dot
将其作为列 vector 处理。
extern crate ndarray; use ndarray::{arr1, arr2, Array1}; fn main() { let scalar = 4; let vector = arr1(&[1, 2, 3]); let matrix = arr2(&[[4, 5, 6], [7, 8, 9]]); let new_vector: Array1<_> = scalar * vector; println!("{}", new_vector); let new_matrix = matrix.dot(&new_vector); println!("{}", new_matrix); }
反转矩阵
使用nalgebra::Matrix3
创建 3x3 矩阵,如果可能的话,将其反转。
extern crate nalgebra; use nalgebra::Matrix3; fn main() { let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0); println!("m1 = {}", m1); match m1.try_inverse() { Some(inv) => { println!("The inverse of m1 is: {}", inv); } None => { println!("m1 is not invertible!"); } } }