diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2024-01-01 19:53:06 +0100 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2024-01-01 19:53:06 +0100 |
| commit | 6c5fe99d41a3d63975deb08911ebb0e162684a43 (patch) | |
| tree | 639b87aaf67fcd3d27c14764b96d5b4e05b0ab58 /2023/24 | |
| parent | 94d0033ed89bb5bfb6500051296892fb7cea6c29 (diff) | |
| download | aoc-6c5fe99d41a3d63975deb08911ebb0e162684a43.tar.gz aoc-6c5fe99d41a3d63975deb08911ebb0e162684a43.zip | |
Improved numerical precision
Diffstat (limited to '2023/24')
| -rw-r--r-- | 2023/24/24b.c | 49 |
1 files changed, 27 insertions, 22 deletions
diff --git a/2023/24/24b.c b/2023/24/24b.c index 48b0a79..90e2e74 100644 --- a/2023/24/24b.c +++ b/2023/24/24b.c | |||
| @@ -4,9 +4,12 @@ machine (ahah - doesn't work on my machine (: ) | |||
| 4 | However, with slightly different values for eps I got two answers whose | 4 | However, with slightly different values for eps I got two answers whose |
| 5 | difference was 4 and one was too high, the other too low. A quick binary | 5 | difference was 4 and one was too high, the other too low. A quick binary |
| 6 | search gave the correct result. | 6 | search gave the correct result. |
| 7 | I'll make a more stable version if I feel like it. EDIT: made slighly more | 7 | I'll make a more stable version if I feel like it. |
| 8 | numerically stable, now the result is actually correct (if rounded to the | 8 | |
| 9 | nearest integer). | 9 | EDIT: made slighly more numerically stable, now the result is actually |
| 10 | correct (if rounded to the nearest integer). | ||
| 11 | |||
| 12 | EDIT: made numerical result even more accurate using long doubles. | ||
| 10 | */ | 13 | */ |
| 11 | 14 | ||
| 12 | #include <stdbool.h> | 15 | #include <stdbool.h> |
| @@ -20,7 +23,8 @@ nearest integer). | |||
| 20 | #define eps 1e-10 | 23 | #define eps 1e-10 |
| 21 | #define EQ(x,y) (ABS((x)-(y))<eps*(ABS(x)+ABS(y))) | 24 | #define EQ(x,y) (ABS((x)-(y))<eps*(ABS(x)+ABS(y))) |
| 22 | 25 | ||
| 23 | typedef struct { double x, y, z; } point_t; | 26 | typedef long double num_t; |
| 27 | typedef struct { num_t x, y, z; } point_t; | ||
| 24 | typedef struct { point_t p, v; } line_t; | 28 | typedef struct { point_t p, v; } line_t; |
| 25 | 29 | ||
| 26 | char *buf, line[N]; | 30 | char *buf, line[N]; |
| @@ -30,9 +34,9 @@ line_t l[N]; | |||
| 30 | bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; } | 34 | bool isnum(char c) { return (c >= '0' && c <= '9') || c == '-'; } |
| 31 | 35 | ||
| 32 | line_t readl(char *buf) { | 36 | line_t readl(char *buf) { |
| 33 | double a[6]; | 37 | num_t a[6]; |
| 34 | for (int i = 0; i < 6; i++) { | 38 | for (int i = 0; i < 6; i++) { |
| 35 | a[i] = (double)atoll(buf); | 39 | a[i] = (num_t)atoll(buf); |
| 36 | while (isnum(*buf)) buf++; | 40 | while (isnum(*buf)) buf++; |
| 37 | while (*buf != '\n' && !isnum(*buf)) buf++; | 41 | while (*buf != '\n' && !isnum(*buf)) buf++; |
| 38 | } | 42 | } |
| @@ -42,25 +46,25 @@ line_t readl(char *buf) { | |||
| 42 | }; | 46 | }; |
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | void swap(double *x, double *y) { double aux = *x; *x = *y; *y = aux; } | 49 | void swap(num_t *x, num_t *y) { num_t aux = *x; *x = *y; *y = aux; } |
| 46 | 50 | ||
| 47 | void printa(double A[D][D], double C[D], int d) { | 51 | void printa(num_t A[D][D], num_t C[D], int d) { |
| 48 | for (int i = 0; i < d; i++) { | 52 | for (int i = 0; i < d; i++) { |
| 49 | printf("[ "); | 53 | printf("[ "); |
| 50 | for (int j = 0; j < d; j++) | 54 | for (int j = 0; j < d; j++) |
| 51 | printf("%.2lf ", A[i][j]); | 55 | printf("%.2llf ", A[i][j]); |
| 52 | printf("]\t[%.2lf]\n", C[i]); | 56 | printf("]\t[%.2llf]\n", C[i]); |
| 53 | } | 57 | } |
| 54 | } | 58 | } |
| 55 | 59 | ||
| 56 | /* Solve the linear system AX = C with row reduction */ | 60 | /* Solve the linear system AX = C with row reduction */ |
| 57 | /* Return true if it has a unique solution, false in any other case */ | 61 | /* Return true if it has a unique solution, false in any other case */ |
| 58 | bool solvesystem(double A[D][D], double C[D], int d, double *X) { | 62 | bool solvesystem(num_t A[D][D], num_t C[D], int d, num_t *X) { |
| 59 | /* Row reduction */ | 63 | /* Row reduction */ |
| 60 | for (int i = 0; i < d; i++) { | 64 | for (int i = 0; i < d; i++) { |
| 61 | /* Make first nonzero */ | 65 | /* Make first nonzero */ |
| 62 | int imax; | 66 | int imax; |
| 63 | double maxi = 0.0; | 67 | num_t maxi = 0.0; |
| 64 | for (int j = i; j < d; j++) { | 68 | for (int j = i; j < d; j++) { |
| 65 | if (ABS(A[j][i]) > maxi) { | 69 | if (ABS(A[j][i]) > maxi) { |
| 66 | maxi = ABS(A[j][i]); | 70 | maxi = ABS(A[j][i]); |
| @@ -74,7 +78,7 @@ bool solvesystem(double A[D][D], double C[D], int d, double *X) { | |||
| 74 | 78 | ||
| 75 | /* Reduce rows */ | 79 | /* Reduce rows */ |
| 76 | for (int ii = i+1; ii < d; ii++) { | 80 | for (int ii = i+1; ii < d; ii++) { |
| 77 | double r = A[ii][i] / A[i][i]; | 81 | num_t r = A[ii][i] / A[i][i]; |
| 78 | for (int k = i; k < d; k++) | 82 | for (int k = i; k < d; k++) |
| 79 | A[ii][k] -= r*A[i][k]; | 83 | A[ii][k] -= r*A[i][k]; |
| 80 | C[ii] -= r*C[i]; | 84 | C[ii] -= r*C[i]; |
| @@ -92,7 +96,7 @@ bool solvesystem(double A[D][D], double C[D], int d, double *X) { | |||
| 92 | return true; | 96 | return true; |
| 93 | } | 97 | } |
| 94 | 98 | ||
| 95 | point_t pos(line_t l, double t) { | 99 | point_t pos(line_t l, num_t t) { |
| 96 | return (point_t) { | 100 | return (point_t) { |
| 97 | .x = l.p.x + t*l.v.x, | 101 | .x = l.p.x + t*l.v.x, |
| 98 | .y = l.p.y + t*l.v.y, | 102 | .y = l.p.y + t*l.v.y, |
| @@ -102,7 +106,7 @@ point_t pos(line_t l, double t) { | |||
| 102 | 106 | ||
| 103 | void testsolution(line_t sol) { | 107 | void testsolution(line_t sol) { |
| 104 | for (int i = 0; i < nlines; i++) { | 108 | for (int i = 0; i < nlines; i++) { |
| 105 | double t = -1.0; | 109 | num_t t = -1.0; |
| 106 | if (!EQ(sol.v.x, l[i].v.x)) | 110 | if (!EQ(sol.v.x, l[i].v.x)) |
| 107 | t = (sol.p.x-l[i].p.x)/(l[i].v.x-sol.v.x); | 111 | t = (sol.p.x-l[i].p.x)/(l[i].v.x-sol.v.x); |
| 108 | if (!EQ(sol.v.y, l[i].v.y)) | 112 | if (!EQ(sol.v.y, l[i].v.y)) |
| @@ -111,8 +115,9 @@ void testsolution(line_t sol) { | |||
| 111 | t = (sol.p.z-l[i].p.z)/(l[i].v.z-sol.v.z); | 115 | t = (sol.p.z-l[i].p.z)/(l[i].v.z-sol.v.z); |
| 112 | point_t p1 = pos(sol, t), p2 = pos(l[i], t); | 116 | point_t p1 = pos(sol, t), p2 = pos(l[i], t); |
| 113 | /* | 117 | /* |
| 114 | printf("Line %d intersected at t = %.2lf " | 118 | printf("Line %d intersected at t = %.2llf in " |
| 115 | "in (%.2lf, %.2lf, %.2lf) and (%.2lf, %.2lf, %.2lf)\n", | 119 | "(%.2llf, %.2llf, %.2llf) and " |
| 120 | "(%.2llf, %.2llf, %.2llf)\n", | ||
| 116 | i, t, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); | 121 | i, t, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); |
| 117 | */ | 122 | */ |
| 118 | if (t < eps || | 123 | if (t < eps || |
| @@ -152,7 +157,7 @@ int main() { | |||
| 152 | * one can shuffle the input file until it works. | 157 | * one can shuffle the input file until it works. |
| 153 | */ | 158 | */ |
| 154 | int d = 6; | 159 | int d = 6; |
| 155 | double A[D][D] = { | 160 | num_t A[D][D] = { |
| 156 | /* First equation: lines 0 and 1, x and y only */ | 161 | /* First equation: lines 0 and 1, x and y only */ |
| 157 | {l[0].v.y-l[1].v.y, l[1].v.x-l[0].v.x, 0.0, | 162 | {l[0].v.y-l[1].v.y, l[1].v.x-l[0].v.x, 0.0, |
| 158 | l[1].p.y-l[0].p.y, l[0].p.x-l[1].p.x, 0.0}, | 163 | l[1].p.y-l[0].p.y, l[0].p.x-l[1].p.x, 0.0}, |
| @@ -172,7 +177,7 @@ int main() { | |||
| 172 | {0.0, l[0].v.z-l[2].v.z, l[2].v.y-l[0].v.y, | 177 | {0.0, l[0].v.z-l[2].v.z, l[2].v.y-l[0].v.y, |
| 173 | 0.0, l[2].p.z-l[0].p.z, l[0].p.y-l[2].p.y}, | 178 | 0.0, l[2].p.z-l[0].p.z, l[0].p.y-l[2].p.y}, |
| 174 | }; | 179 | }; |
| 175 | double C[D] = { | 180 | num_t C[D] = { |
| 176 | /* First equation: lines 0 and 1, x and y only */ | 181 | /* First equation: lines 0 and 1, x and y only */ |
| 177 | l[1].p.y*l[1].v.x - l[1].p.x*l[1].v.y | 182 | l[1].p.y*l[1].v.x - l[1].p.x*l[1].v.y |
| 178 | - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y, | 183 | - l[0].p.y*l[0].v.x + l[0].p.x*l[0].v.y, |
| @@ -192,7 +197,7 @@ int main() { | |||
| 192 | l[2].p.z*l[2].v.y - l[2].p.y*l[2].v.z | 197 | l[2].p.z*l[2].v.y - l[2].p.y*l[2].v.z |
| 193 | - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z, | 198 | - l[0].p.z*l[0].v.y + l[0].p.y*l[0].v.z, |
| 194 | }; | 199 | }; |
| 195 | double X[d]; | 200 | num_t X[d]; |
| 196 | if (!solvesystem(A, C, d, X)) { | 201 | if (!solvesystem(A, C, d, X)) { |
| 197 | printf("No unique solution, shuffle input and try again.\n"); | 202 | printf("No unique solution, shuffle input and try again.\n"); |
| 198 | exit(1); | 203 | exit(1); |
| @@ -204,8 +209,8 @@ int main() { | |||
| 204 | }; | 209 | }; |
| 205 | testsolution(sol); | 210 | testsolution(sol); |
| 206 | 211 | ||
| 207 | printf("p = (%.2lf, %.2lf, %.2lf), v = (%.2lf, %.2lf, %.2lf)\n", | 212 | printf("p = (%.2llf, %.2llf, %.2llf), v = (%.2llf, %.2llf, %.2llf)\n", |
| 208 | sol.p.x, sol.p.y, sol.p.z, sol.v.x, sol.v.y, sol.v.z); | 213 | sol.p.x, sol.p.y, sol.p.z, sol.v.x, sol.v.y, sol.v.z); |
| 209 | printf("%lf\n", sol.p.x + sol.p.y + sol.p.z); | 214 | printf("%llf\n", sol.p.x + sol.p.y + sol.p.z); |
| 210 | return 0; | 215 | return 0; |
| 211 | } | 216 | } |
