I had to do some Gaussian elimination for an assignment. So here's Gaussian
elimination in Ruby:
# Performs an in-place Gaussian elimination on an NxN matrix 'matrix' (2D array
# of Numeric objects) and an N-element vector 'vector.' (array of N Numerics).
def gaussianElimination(matrix, vector)
0.upto(matrix.length - 2) do |pivotIdx|
# Find the best pivot. This is the one who has the largest absolute value
# relative to his row (scaled partial pivoting). This step can be omitted
# to improve speed at the cost of increased error.
maxRelVal = 0
maxIdx = pivotIdx
(pivotIdx).upto(matrix.length - 1) do |row|
relVal = matrix[row][pivotIdx] / matrix[row].map{ |x| x.abs }.max
if relVal >= maxRelVal
maxRelVal = relVal
maxIdx = row
end
end
# Swap the best pivot row into place.
matrix[pivotIdx], matrix[maxIdx] = matrix[maxIdx], matrix[pivotIdx]
vector[pivotIdx], vector[maxIdx] = vector[maxIdx], vector[pivotIdx]
pivot = matrix[pivotIdx][pivotIdx]
# Loop over each row below the pivot row.
(pivotIdx+1).upto(matrix.length - 1) do |row|
# Find factor so that [this row] = [this row] - factor*[pivot row]
# leaves 0 in the pivot column.
factor = matrix[row][pivotIdx]/pivot
# We know it will be zero.
matrix[row][pivotIdx] = 0.0
# Compute [this row] = [this row] - factor*[pivot row] for the other cols.
(pivotIdx+1).upto(matrix[row].length - 1) do |col|
matrix[row][col] -= factor*matrix[pivotIdx][col]
end
vector[row] -= factor*vector[pivotIdx]
end
end
return [matrix,vector]
end
# Assumes 'matrix' is in row echelon form.
def backSubstitution(matrix, vector)
(matrix.length - 1).downto( 0 ) do |row|
tail = vector[row]
(row+1).upto(matrix.length - 1) do |col|
tail -= matrix[row][col] * vector[col]
matrix[row][col] = 0.0
end
vector[row] = tail / matrix[row][row]
matrix[row][row] = 1.0
end
end
# Example usage:
require 'pp'
# A system of equations: matrix * X = vector
matrix =
[
[1.0, 1.0, 1.0, 1.0],
[0.0, 1.0, 2.0, 3.0],
[1.0, 2.0, 4.0, 8.0],
[0.0, 1.0, 4.0, 12.0],
]
vector = [1.0, 0.0, 2.0, 0.0]
# Create a backup for verification.
matrix_backup = Marshal.load(Marshal.dump(matrix))
vector_backup= vector.dup
# Gaussian elemination to put the system in row echelon form.
gaussianElimination(matrix, vector)
# Back-substitution to solve the system.
backSubstitution(matrix, vector)
# Print the result.
pp matrix
pp vector
# Verify the result.
pass = true
0.upto(matrix_backup.length - 1) do |eqn|
sum = 0
0.upto(matrix_backup[eqn].length - 1) do |term|
sum += matrix_backup[eqn][term] * vector[term]
end
if (sum - vector_backup[eqn]).abs > 0.0000000001
pass = false
break
end
end
if pass
puts "Verification PASSED."
else
puts "Verification FAILED."
end