0
0

More than 1 year has passed since last update.

行列の積で見る2次元配列の例(Julia, Go, Rust)

Last updated at Posted at 2021-12-23

はじめに

別の言語の例を移植すると意外と考えさせられる。

環境

  • The Go Playground (2021-12-23)
  • Julia 1.6 (Julia Playground)
  • Rust 1.5.7 (Rust Playground; rustc)

Rust は手元に環境があるのだが、サンプル程度でプロジェクトを作ったりのが面倒なときは、Playground を利用する。

Julia

計算科学用途な言語だけあって、こういうところは強い。
この宣言の時点で行列の型だと決めつけができる。

A = [1 2 1; 0 1 1]
B = [1 0; 0 1; 1 1]
println(A*B)
[2 3; 1 2]

Go

  • 2次元配列
    関数も含めてサイズを事前に決めておく必要があり、その結果、任意のサイズの行列の積を行う関数が作れない

    package main
    
    import "fmt"
    
    func mul(a [2][3]int, b [3][2]int) [2][2]int {
        var i, j, k int
        var c [len(a)][len(b[0])]int
        for i = 0; i < len(a); i++ {
            for k = 0; k < len(b[0]); k++ {
                for j = 0; j < len(a[0]); j++ {
                    c[i][k] += a[i][j] * b[j][k]
                }
            }
        }
        return c
    }
    
    func main() {
        var a = [2][3]int{
            {1, 2, 1},
            {0, 1, 1},
        }
        var b = [3][2]int{
            {1, 0},
            {0, 1},
            {1, 1},
        }
    
        var c = mul(a, b)
        fmt.Println(c)
        fmt.Printf("%d %d\n", c[0][0], c[0][1])
        fmt.Printf("%d %d\n", c[1][0], c[1][1])
    }
    

    実行結果

    [[2 3] [1 2]]
    2 3
    1 2
    
  • 2次元スライス
    スライスであればサイズを指定しない宣言ができため、任意のサイズの行列の積を行う関数を実現できる。

    package main
    
    import "fmt"
    
    func mul(a [][]int, b [][]int) [][]int {
        var j, k int
        c := make([][]int, len(a))
        for i := range a {
            c[i] = make([]int, len(b[0]))
            for k = 0; k < len(b[0]); k++ {
                for j = 0; j < len(a[0]); j++ {
                    c[i][k] += a[i][j] * b[j][k]
                }
            }
        }
        return c
    }
    
    func main() {
        var a = make([][]int, 2)
        a[0] = []int{1, 2, 1}
        a[1] = []int{0, 1, 1}
        var b = make([][]int, 3)
        b[0] = []int{1, 0}
        b[1] = []int{0, 1}
        b[2] = []int{1, 1}
    
        var c = mul(a, b)
        fmt.Println(c)
        fmt.Printf("%d %d\n", c[0][0], c[0][1])
        fmt.Printf("%d %d\n", c[1][0], c[1][1])
    }
    

    実行結果

    [[2 3] [1 2]]
    2 3
    1 2
    

Rust

C/C++辺りで実装すると必要な malloc/free, new/delete が不要なのは楽。

  • Vec を使った例
    Rust の制約として初期化が必要(この辺り、非効率に思えるんだが最適化次第な気がする。)

    fn mul(a: &Vec<Vec<i32>>, b: &Vec<Vec<i32>>) -> Vec<Vec<i32>> {
        let mut result = vec![vec![0; a.len()]; b[0].len()];
    
        for i in 0..a.len() {
            for k in 0..b[0].len() {
                for j in 0..a[0].len() {
                    result[i][k] += a[i][j] * b[j][k];
                }
            }
        }
        result
    }
    
    fn main() {
        let a = vec![
            vec![1, 2, 1],
            vec![0, 1, 1]
        ];
        let b = vec![
            vec![1, 0],
            vec![0, 1],
            vec![1, 1]
        ];
        let c = mul(&a, &b);
        println!("c={:?}", c);
    }
    

    実行結果

    c=[[2, 3], [1, 2]]
    
  • Generics
    0 で初期化する部分は、T::zero() を実装している必要がある。
    Copy がないと a[i][j]b[j][k] で所有権の移動が行われて処理が成立しない。
    あとは、 *+= のTraitを実装している必要がある。

    fn mul<T>(a: &Vec<Vec<T>>, b: &Vec<Vec<T>>) -> Vec<Vec<T>>
    where T: num::traits::Zero, T: Copy,
          T: std::ops::Mul<Output = T>, T: std::ops::AddAssign<T>
    {
        let mut result = vec![vec![T::zero(); a.len()]; b[0].len()];
    
        for i in 0..a.len() {
            for k in 0..b[0].len() {
                for j in 0..a[0].len() {
                    result[i][k] += a[i][j] * b[j][k];
                }
            }
        }
        result
    }
    
    fn main() {
        let a = vec![
            vec![1, 2, 1],
            vec![0, 1, 1]
        ];
        let b = vec![
            vec![1, 0],
            vec![0, 1],
            vec![1, 1]
        ];
        let c = mul::<u32>(&a, &b);
        println!("c={:?}", c);
    }
    

    実行結果

    c=[[2, 3], [1, 2]]
    
  • Nalgebra
    SMatrix::<u32, 2, 3>Matrix2x3 でもよい。

    extern crate nalgebra as na;
    
    fn main() {
        let a = na::SMatrix::<u32, 2, 3>::new(
            1, 2, 1,
            0, 1, 1
        );
        let b = na::SMatrix::<u32, 3, 2>::new(
            1, 0,
            0, 1,
            1, 1
        );
        let c = a * b;
        println!("{:?}", c);
        println!(
            "{:?} {:?}\n{:?} {:?}",
            c[(0, 0)], c[(0, 1)], c[(1, 0)], c[(1, 1)]
        );
    }
    

    実行結果

    Matrix { data: [[2, 1], [3, 2]] }
    2 3
    1 2
    
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0