Skip to content

Commit 507da09

Browse files
Gathroszsparal
authored andcommitted
Adding Gaussian Elimination and Thomas Algorithm in C. (#195)
* Adding gaussian elimination in c * updating gaussian_elimination.md * adding thomas.c * updating thomas.md * small change to thomas.c
1 parent 6d02784 commit 507da09

File tree

4 files changed

+134
-0
lines changed

4 files changed

+134
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <math.h>
4+
5+
void swap_rows(double * const a, size_t i, size_t pivot, size_t cols) {
6+
for (size_t j = 0; j < cols; ++j) {
7+
double tmp = a[i * cols + j];
8+
a[i * cols + j] = a[pivot * cols + j];
9+
a[pivot * cols + j] = tmp;
10+
}
11+
}
12+
13+
void gaussian_elimination(double *a, const size_t rows, const size_t cols) {
14+
size_t min_dim = (rows < cols)? rows: cols;
15+
16+
for (size_t k = 0; k < min_dim; ++k) {
17+
size_t pivot = k;
18+
19+
for (size_t i = k + 1; i < rows; ++i) {
20+
if (fabs(a[i * cols + k]) > fabs(a[pivot * cols + k])) {
21+
pivot = i;
22+
}
23+
}
24+
25+
if (a[pivot * cols + k] == 0) {
26+
printf("The matrix is singular.\n");
27+
exit(0);
28+
}
29+
30+
if (k != pivot) {
31+
swap_rows(a, k, pivot, cols);
32+
}
33+
34+
for (size_t i = k + 1; i < rows; ++i) {
35+
double scale = a[i * cols + k] / a[k * cols + k];
36+
37+
for (size_t j = k + 1; j < cols; ++j) {
38+
a[i * cols + j] -= a[k * cols + j] * scale;
39+
}
40+
41+
a[i * cols + k] = 0;
42+
}
43+
}
44+
}
45+
46+
void back_substitution(const double * const a, double * const x,
47+
const size_t rows, const size_t cols) {
48+
49+
for (int i = rows - 1; i >= 0; --i) {
50+
double sum = 0.0;
51+
52+
for (size_t j = cols - 2; j > i; --j) {
53+
sum += x[j] * a[i * cols + j];
54+
}
55+
56+
x[i] = (a[i * cols + cols - 1] - sum) / a[i * cols + i];
57+
}
58+
}
59+
60+
int main() {
61+
double a[3][4] = {{3.0, 2.0, -4.0, 3.0},
62+
{2.0, 3.0, 3.0, 15.0},
63+
{5.0, -3.0, 1.0, 14.0}};
64+
65+
gaussian_elimination((double *)a, 3, 4);
66+
67+
for (size_t i = 0; i < 3; ++i) {
68+
printf("[");
69+
for (size_t j = 0; j < 4; ++j) {
70+
printf("%f ", a[i][j]);
71+
}
72+
printf("]\n");
73+
}
74+
75+
printf("\n");
76+
77+
double x[3] = {0, 0, 0};
78+
back_substitution((double *)a, x, 3, 4);
79+
80+
printf("(%f,%f,%f)\n", x[0], x[1], x[2]);
81+
82+
return 0;
83+
}

chapters/matrix_methods/gaussian_elimination/gaussian_elimination.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ In code, this looks like:
257257
{% method %}
258258
{% sample lang="jl" %}
259259
[import:1-42, lang:"julia"](code/julia/gaussian_elimination.jl)
260+
{% sample lang="c" %}
261+
[import:13-44, lang:"c_cpp"](code/c/gaussian_elimination.c)
260262
{% endmethod %}
261263

262264
As with all code, it takes time to fully absorb what is going on and why everything is happening; however, I have tried to comment the above psuedocode with the necessary steps. Let me know if anything is unclear!
@@ -270,6 +272,8 @@ Even though this seems straightforward, the pseudocode might not be as simple as
270272
{% method %}
271273
{% sample lang="jl" %}
272274
[import:44-64, lang:"julia"](code/julia/gaussian_elimination.jl)
275+
{% sample lang="c" %}
276+
[import:46-58, lang:"c_cpp"](code/c/gaussian_elimination.c)
273277
{% endmethod %}
274278

275279
Now, as for what's next... Well, we are in for a treat! The above algorithm clearly has 3 `for` loops, and will thus have a complexity of $$\sim O(n^3)$$, which is abysmal! If we can reduce the matrix to a specifically **tridiagonal** matrix, we can actually solve the system in $$\sim O(n)$$! How? Well, we can use an algorithm known as the _Tri-Diagonal Matrix Algorithm_ \(TDMA\) also known as the _Thomas Algorithm_.
@@ -281,6 +285,8 @@ The full code can be seen here:
281285
{% method %}
282286
{% sample lang="jl" %}
283287
[import, lang:"julia"](code/julia/gaussian_elimination.jl)
288+
{% sample lang="c" %}
289+
[import, lang:"c_cpp"](code/c/gaussian_elimination.c)
284290
{% sample lang="rs" %}
285291
[import, lang:"rust"](code/rust/gaussian_elimination.rs)
286292
{% endmethod %}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <stdio.h>
2+
#include <string.h>
3+
4+
void thomas(double * const a, double * const b, double * const c,
5+
double * const x, const size_t size) {
6+
7+
double y[size];
8+
memset(y, 0, size * sizeof(double));
9+
10+
y[0] = c[0] / b[0];
11+
x[0] = x[0] / b[0];
12+
13+
for (size_t i = 1; i < size; ++i) {
14+
double scale = 1.0 / (b[i] - a[i] * y[i - 1]);
15+
y[i] = c[i] * scale;
16+
x[i] = (x[i] - a[i] * x[i - 1]) * scale;
17+
}
18+
19+
for (int i = size - 2; i >= 0; --i) {
20+
x[i] -= y[i] * x[i + 1];
21+
}
22+
}
23+
24+
int main() {
25+
double a[] = {0.0, 2.0, 3.0};
26+
double b[] = {1.0, 3.0, 6.0};
27+
double c[] = {4.0, 5.0, 0.0};
28+
double x[] = {7.0, 5.0, 3.0};
29+
30+
printf("The system,\n");
31+
printf("[1.0 4.0 0.0][x] = [7.0]\n");
32+
printf("[2.0 3.0 5.0][y] = [5.0]\n");
33+
printf("[0.0 3.0 6.0][z] = [3.0]\n");
34+
printf("has the solution:\n");
35+
36+
thomas(a, b, c, x, 3);
37+
38+
for (size_t i = 0; i < 3; ++i) {
39+
printf("[%f]\n", x[i]);
40+
}
41+
42+
return 0;
43+
}

chapters/matrix_methods/thomas/thomas.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ In code, this will look like this:
4040
{% method %}
4141
{% sample lang="jl" %}
4242
[import, lang:"julia"](code/julia/thomas.jl)
43+
{% sample lang="c" %}
44+
[import, lang:"c_cpp"](code/c/thomas.c)
4345
{% sample lang="py" %}
4446
[import, lang:"python"](code/python/thomas.py)
4547
{% endmethod %}

0 commit comments

Comments
 (0)