多維矩陣降維

December 12, 2021

有時為了運算方便或儲存時空間問題,會將多維矩陣降維,例如,是將多面體的頂點攤平,並結合頂點索引來定義多面體,或者將上三角、下三角或對角矩陣降一維陣列,以節省空間,

解法思路

以二維陣列轉一維陣列為例,索引由 0 開始,看要以列為主(row-major)或以行為主(column-major)。

以列為主的二維陣列要降為一維時,是將二維陣列由上往下,逐列寫入一維陣列,此時索引的對應公式如下,其中 rowcolumn 是二維陣列索引,loc 表示對應的一維陣列索引:

loc = column + row * 行數

以行為主的二維陣列要降為一維時,是將二維陣列由左往右,逐行寫入一維陣列,此時索引的對應公式如下:

loc = row + column * 列數

若是三維陣列,公式如下,其中 i(個數 u1)、j(個數 u2)、k(個數 u3)分別表示三維陣列的三個索引:
以列為主:loc = i * u2 * u3 + j * u3 + k
以行為主:loc = k * u1* u2 + j * u1 + i

更高維度的可以自行類推,不過更高維度時,建議使用其他資料結構,比較具體也不易搞錯。

程式實作

#include <stdio.h> 
#include <stdlib.h>
#define ROW 3
#define COLUMN 4

void toOneByRow(int[][], int[]);
void toOneByColumn(int[][], int[]);
int indexByRow(int, int);
int indexByColumn(int, int);
void toOne(int[][COLUMN], int[], int (*)(int, int));

int main(void) { 
    int arr1[ROW][COLUMN] = {{1, 2, 3, 4}, 
                             {5, 6, 7, 8}, 
                             {9, 10, 11, 12}}; 
    int arr2[ROW * COLUMN] = {0}; 

    printf("原二維資料:\n"); 
    int row, column;
    for(row = 0; row < 3; row++) { 
        for(column = 0; column < 4; column++) { 
            printf("%4d", arr1[row][column]); 
        } 
        printf("\n"); 
    } 

    printf("\n以列為主:"); 
    toOneByRow(arr1, arr2);
    int i;
    for(i = 0; i < 12; i++) 
        printf("%d ", arr2[i]); 

    printf("\n以行為主:"); 
    toOneByColumn(arr1, arr2);
    for(i = 0; i < 12; i++) 
        printf("%d ", arr2[i]); 

    printf("\n"); 

    return 0; 
} 

void toOneByRow(int arr1[][COLUMN], int arr2[]) {
    toOne(arr1, arr2, indexByRow);
}

void toOneByColumn(int arr1[][COLUMN], int arr2[]) {
    toOne(arr1, arr2, indexByColumn);
}

int indexByRow(int row, int column) {
    return column + row * COLUMN;
}

int indexByColumn(int row, int column) {
    return row + column * ROW;
}

void toOne(int arr1[][COLUMN], int arr2[], int (*index)(int, int)) {
    int row, column;
    for(row = 0; row < ROW; row++) { 
        for(column = 0; column < COLUMN; column++) { 
            arr2[index(row, column)] = arr1[row][column]; 
        } 
    }    
}
public class Matrix {   
    public static int[] toOneByRow(final int[][] array) {
        return toOne(array, new Index() {
            public int get(int row, int column) {
                return column + row * array[0].length; 
            }
        });
    }
    
    public static int[] toOneByColumn(final int[][] array) {
        return toOne(array, new Index() {
            public int get(int row, int column) {
                return row + column * array.length; 
            }
        });
    }

    private static interface Index {
        int get(int row, int column);
    }
    
    private static int[] toOne(int[][] array, Index index) {
        int[] arr = new int[array.length * array[0].length];
        for(int row = 0; row < array.length; row++) { 
            for(int column = 0; column < array[0].length; column++) { 
                arr[index.get(row, column)] = array[row][column]; 
            } 
        }
        return arr;
    }
}
def toOneByRow(array):
    return toOne(array, (lambda row, column: column + row * len(array[0])))

def toOneByColumn(array):
    return toOne(array, (lambda row, column: row + column * len(array)))

def toOne(array, f):
    arr = [0] * (len(array) * len(array[0]))
    for row in range(len(array)):
        for column in range(len(array[0])):
            arr[f(row, column)] = array[row][column]
    return arr
object Matrix {
    def toOneByRow(array: Array[Array[Int]]) = {
        toOne(array, _ * array(0).length + _)
    }
    
    def toOneByColumn(array: Array[Array[Int]]) = {
        toOne(array, _ + _ * array.length)
    }
    
    private def toOne(array: Array[Array[Int]], f: (Int, Int) => Int) = {
        val arr = new Array[Int](array.length * array(0).length)
        for(row <- 0 until array.length; column <- 0 until array(0).length) {
            arr(f(row, column)) = array(row)(column)
        }
        arr    
    }
}
def toOneByRow(array)
    toOne(array) { |row, column| column + row * array[0].length }
end

def toOneByColumn(array)
    toOne(array) { |row, column| row + column * array.length }
end

def toOne(array)
    arr = Array.new(array.length * array[0].length, 0)
    array.length.times { |row|
        array[0].length.times { |column|
            arr[yield(row, column)] = array[row][column]
        }
    }
    arr
end