Skip to content

Commit

Permalink
Refactoring with sympy for d24
Browse files Browse the repository at this point in the history
  • Loading branch information
derailed-dash committed Apr 27, 2024
1 parent 0acbb0a commit b1ea519
Showing 1 changed file with 27 additions and 27 deletions.
54 changes: 27 additions & 27 deletions src/AoC_2023/Dazbo's_Advent_of_Code_2023.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9792,7 +9792,7 @@
"\n",
"By the way, here's a neat trick! If you'd rather not programmatically render the graph, e.g. with NetworkX, you can simply output the values of the adjacency dictionary, and then copy/paste them into the [CS Academy Graph Editor](https://csacademy.com/app/graph_editor/). That way, you an quickly visualise your graph.\n",
"\n",
"![CS Academy Graph Editor](https://aoc.just2good.co.uk/assets/images/csacademy-graph-editor.png)\n",
"<img src=\"https://aoc.just2good.co.uk/assets/images/csacademy-graph-editor.png\" alt=\"CS Academy Graph Editor\" width=\"768\">\n",
"\n",
"Now we can use a _Depth First Search (DFS)_ to **explore all paths from start to end.** We have to use DFS rather than BFS, because the BFS would give us the _shortest path_. But we need the longest path. So we need to explore all paths.\n",
"\n",
Expand Down Expand Up @@ -10062,7 +10062,6 @@
"\n",
"# SETUP LOGGING\n",
"logger.setLevel(logging.DEBUG)\n",
"# td.setup_file_logging(logger, locations.output_dir)\n",
"\n",
"# Retrieve input and store in local file\n",
"try:\n",
Expand Down Expand Up @@ -10116,26 +10115,25 @@
"- So now we have the `mx` component of `y = mx + c`. But we still need to determine `c`. So let's rearrange:\n",
"\n",
"$$\n",
"\\begin{align}\n",
"\\begin{align*}\n",
"y &= mx + c \\\\\n",
"c &= y - mx\n",
"\\end{align}\n",
"\\end{align*}\n",
"$$\n",
"\n",
"- I've added a `y_intercept()` method to my class, to return this value.\n",
"- Now that we have everything we need to express any given `Hailstone` trajectory as a line `y = mx + c`, we're ready to find the intersection between two such lines. Here, we have two lines, labelled `a` and `b`:\n",
"- Now that we have everything we need to express any given `Hailstone` trajectory as a line `y = mx + c`, we're ready to find the intersection between two such lines. Specifically, the value of `x` where the lines intersect. Here, we have two lines, labelled `a` and `b`:\n",
"\n",
"$$\n",
"\\begin{align}\n",
"\\begin{align*}\n",
"y_{a} &= m_{a}x + c_{a} \\\\\n",
"y_{b} &= m_{b}x + c_{b} \\\\\n",
"\\\\\n",
"\\text{At the intersection:} \\\\\n",
"m_{a}x + c_{a} &= m_{b}x + c_{b} \\\\\n",
"m_{a}x - m_{b}x &= c_{b} - c_{a} \\\\\n",
"x(m_{a} - m_{b}) = c_{b} - c_{a} \\\\\n",
"x = \\frac{c_{b} - c_{a}}{m_{a} - m_{b}} \\\\\n",
"\\end{align}\n",
"x(m_{a} - m_{b}) &= c_{b} - c_{a} \\\\\n",
"x &= \\frac{c_{b} - c_{a}}{m_{a} - m_{b}} \\\\\n",
"\\end{align*}\n",
"$$\n",
"\n",
"- So this is exactly what my `intersects_with(hailstone)` does. And after it determines the value of `x`, it substitutes this back into `y = mx + c` to determine the corresponding value of `y`. It then returns the intersection location as the `(x,y)` tuple.\n",
Expand All @@ -10148,7 +10146,7 @@
"t_{x} = \\frac{x_{intersection} - x_{initial}}{v_{x}}\n",
"$$\n",
"\n",
"Also: because this problem is simply looking for a crossover of two trajectories, rather than an intersection at a given point in time, then two hailstones might be at the intersection point at different times. So we need to determine _both_ times.\n",
"Also: because this problem is simply **looking for a crossover of two trajectories, rather than an intersection at a given point in time, then two hailstones might be at the intersection point at different times. So we need to determine _both_ times.**\n",
"\n",
"So finally, to solve:\n",
"\n",
Expand Down Expand Up @@ -10228,8 +10226,8 @@
" stones = []\n",
" for row in data:\n",
" posn, velocity = row.split(\"@\")\n",
" posn = [int(val) for val in posn.split(\",\")]\n",
" velocity = [int(val) for val in velocity.split(\",\")]\n",
" posn = [int(val) for val in posn.split(\",\")] # list of x,y,z int\n",
" velocity = [int(val) for val in velocity.split(\",\")] # list of x,y,z int\n",
" stones.append(Hailstone(tuple(posn), tuple(velocity)))\n",
" return stones"
]
Expand Down Expand Up @@ -10257,7 +10255,7 @@
" and coord_min <= intersect[1] <= coord_max\n",
" and t_a >= 0 and t_b >= 0):\n",
" intersections += 1\n",
" # logger.debug(f\"{intersect} at {t_a=}, {t_b=}\")\n",
" \n",
" except ValueError as e:\n",
" logger.debug(e) # e.g. parallel lines\n",
" \n",
Expand Down Expand Up @@ -10285,6 +10283,7 @@
"\n",
"logger.info(\"Tests passed!\")\n",
"\n",
"logger.setLevel(logging.INFO)\n",
"soln = solve_part1(input_data, 200000000000000, 400000000000000)\n",
"logger.info(f\"Part 1 soln={soln}\")"
]
Expand Down Expand Up @@ -10342,7 +10341,7 @@
"\n",
"Now we have a set of equations that are true for any given hailstone, we can loop through hailstones and build up a set of equestions that must be true.\n",
"\n",
"Here I'm using the awesome SymPy library, which allows us to [solve equations](https://docs.sympy.org/latest/guides/solving/index.html#solving-guide). We can use the `solve()` function to [find the solution to a system of equations](https://docs.sympy.org/latest/guides/solving/solve-system-of-equations-algebraically.html). \n",
"Here I'm using the awesome SymPy library, which allows us to [solve equations](https://docs.sympy.org/latest/guides/solving/index.html#solving-guide). We can use the `solve()` function to [find the solution to a system of equations](https://docs.sympy.org/latest/guides/solving/solve-system-of-equations-algebraically.html). By the way, I've written a quick introduction to _SymPy_ [here](https://medium.com/python-in-plain-english/solving-mathematical-problems-in-python-with-sympy-5f138c0deaef).\n",
"\n",
"Here's how we use it:\n",
"\n",
Expand All @@ -10368,26 +10367,27 @@
" \n",
" # define SymPy rock symbols - these are our unknowns representing:\n",
" # initial rock location (xr, yr, zr) and rock velocity (vxr, vyr, vzr)\n",
" xr, yr, zr, vxr, vyr, vzr = sympy.symbols(\"xr yr zr vxr vyr vzr\")\n",
" xr, yr, zr, vxr, vyr, vzr = sympy.symbols(\"xr yr zr vxr vyr vzr\", integer=True)\n",
" \n",
" equations = [] # we assemble a set of equations that must be true\n",
" for stone in stones[:10]: # we don't need ALL the stones to find a solution. We need just enough.\n",
" x, y, z = stone.posn\n",
" vx, vy, vz = stone.velocity\n",
" # equations.append(sympy.Eq((xr-x)/(vx-vxr), (yr-y)/(vy-vyr)))\n",
" # equations.append(sympy.Eq((yr-y)/(vy-vyr), (zr-z)/(vz-vzr)))\n",
" # equations.append((xr-x)/(vx-vxr) - (yr-y)/(vy-vyr))\n",
" # equations.append((yr-y)/(vy-vyr) - (zr-z)/(vz-vzr))\n",
" # Cross-multiply...\n",
" equations.append(sympy.Eq((xr-x)*(vy-vyr), (yr-y)*(vx-vxr)))\n",
" equations.append(sympy.Eq((yr-y)*(vz-vzr), (zr-z)*(vy-vyr)))\n",
" \n",
" solutions = sympy.solve(equations, dict=True) # SymPy does the hard work\n",
" if solutions:\n",
" solution = solutions[0]\n",
" logger.info(solution)\n",
" return sum([solution[xr], solution[yr], solution[zr]])\n",
" \n",
" logger.info(\"No solutions found.\")\n",
" return None\n"
"\n",
" try:\n",
" solution = sympy.solve(equations, dict=True)[0] # SymPy does the hard work\n",
" logger.debug(f\"{solution=}\")\n",
" return sum([solution[xr], solution[yr], solution[zr]]) \n",
" except (sympy.SympifyError, IndexError) as e:\n",
" logger.error(f\"Could not find a solution. Original exception: {e}\")\n",
"\n",
" return None\n",
" "
]
},
{
Expand Down

0 comments on commit b1ea519

Please sign in to comment.