Study of the algebra of vector spaces.
Gaussian Elimination
It is a fairly simple thing to implement the algorithm for Gaussian Elimination in a manner that produces reasonably stable results. Perfect for reducing simple matrices.
#include <stdio.h> #include <stdint.h> #include <math.h> typedef double f64; void MatrixPrint(f64* matrix, int row_count, int col_count) { for (int row_idx = 0; row_idx < row_count; ++row_idx) { printf("["); for (int col_idx = 0; col_idx < col_count; ++col_idx) { if (col_idx > 0) { printf("\t"); } printf("%0.02f", matrix[col_idx + (row_idx * col_count)]); } printf("]\n"); } } void MatrixSwapRows(f64* matrix, int row_count, int col_count, int swap_row_a, int swap_row_b) { f64* row_a = &matrix[swap_row_a * col_count]; f64* row_b = &matrix[swap_row_b * col_count]; for (int col_idx = 0; col_idx < col_count; ++col_idx) { f64 tmp = row_a[col_idx]; row_a[col_idx] = row_b[col_idx]; row_b[col_idx] = tmp; } } void MatrixRowReduce(f64* matrix, int row_count, int col_count) { #define MatE(R,C) matrix[(C) + ((R) * col_count)] /* Reduce the matrix to row-echelon form */ int pivot_row = 0; int pivot_col = 0; while (pivot_row < row_count && pivot_col < col_count) { /* Find the row with the largest value in its pivot_col to use as next pivot_row */ int next_row_idx = -1; { f64 last_value = 0; for (int row_idx = pivot_row; row_idx < row_count; ++row_idx) { f64 value = MatE(row_idx, pivot_col); if (fabs(value) == 0.0) { continue; } if (fabs(value) > fabs(last_value)) { last_value = value; next_row_idx = row_idx; } } } if (next_row_idx == -1) { /* No pivot in this column so pass to the next column */ ++pivot_col; } else { MatrixSwapRows(matrix, row_count, col_count, pivot_row, next_row_idx); /* Eliminate all rows below the pivot */ for (int row_idx = pivot_row + 1; row_idx < row_count; ++row_idx) { f64 ratio = MatE(row_idx, pivot_col) / MatE(pivot_row, pivot_col); /* Clear current element and update all the elements after it in this row */ MatE(row_idx, pivot_col) = 0.0; for (int col_idx = pivot_col + 1; col_idx < col_count; ++col_idx) { MatE(row_idx, col_idx) -= ratio * MatE(pivot_row, col_idx); } } ++pivot_row; ++pivot_col; } } /* Eliminate upwards */ for (int row_idx = 1; row_idx < row_count; ++row_idx) { int curr_col_idx = row_idx; f64 ratio = MatE(row_idx - 1, curr_col_idx) / MatE(row_idx, curr_col_idx); matrix[curr_col_idx + ((row_idx - 1) * col_count)] = 0.0; for (int col_idx = curr_col_idx + 1; col_idx < col_count; ++col_idx) { MatE(row_idx - 1, col_idx) -= ratio * MatE(row_idx, col_idx); } } /* Finally normalize the rows so we have it in row-reduced echelon form */ for (int row_idx = 0; row_idx < row_count; ++row_idx) { f64 value = matrix[row_idx + (row_idx * col_count)]; if (fabs(value) == 0.0) { continue; } f64 ratio = 1.0 / value; for (int col_idx = row_idx; col_idx < col_count; ++col_idx) { matrix[col_idx + (row_idx * col_count)] *= ratio; } } #undef MatE }
incoming(1): oscillators