-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 68da7a9
Showing
6 changed files
with
697 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
/Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "grambulate" | ||
version = "0.1.0" | ||
edition = "2021" | ||
description = "An implementation of grambulation for positive integers in Rust." | ||
license = "MIT" | ||
repository = "https://github.com/MatsFangohr/grambulate-rust" | ||
|
||
[dependencies] | ||
integer-sqrt = "0.1.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023-present Mats Fangohr | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# grambulate-rust | ||
|
||
An implementation of grambulation in Rust, as initially explained [on Reddit](https://www.reddit.com/r/mathmemes/comments/tvn2gj/the_solution_to_the_april_fools_math/) and more precisely defined for [Code Golf](https://codegolf.stackexchange.com/questions/259698/implement-grambulation). | ||
|
||
## Implementation | ||
|
||
To grambulate two numbers $a$ and $b$ to a solution $c$, we must find the coordinates $c'$ of the solution, for which we must first determine the coordinates of $a$ and $b$ ($a'$ and $b'$) on the spiral. | ||
|
||
To do so, we can calculate the 'rings' $r_a$ and $r_b$ the numbers are in with the formula | ||
$$r(x)=\left\lfloor{\frac{\left\lceil{\sqrt{x}}\right\rceil}{2}}\right\rfloor$$ | ||
Then, the formula | ||
$$v_d(r)=4r^2-(5-d)\cdot{}r+1$$ | ||
can be used to to calculate the four values on the four main diagonals for ring $x$, where $d$ specifies which diagonal to use. To do this, we specify the number on a given diagonal in ring 1 as $d$: | ||
|
||
| Diagonal | $d$ | | ||
| ------------ | --- | | ||
| top-right | 3 | | ||
| top-left | 5 | | ||
| bottom-left | 7 | | ||
| bottom-right | 9 | | ||
|
||
This gives us 4 values near our desired value (either $a$ or $b$). If we find the value closest to but larger than or equal to our target value, we can calculate the difference and apply it as either an x or y offset, giving us the coordinates of our desired value. | ||
|
||
Now that we have both pairs of coordinates, we can calculate the connecting vector $\vec{v}$ and apply to it to $b'$ to find $c'$. | ||
|
||
Once we know $c'$, we know that the ring is the larger of the absolute x and y values. | ||
|
||
We now determine which diagonal we need to calculate, retrieve the value using the above formula and subtract the difference of the relevant coordinate. | ||
|
||
## Example | ||
|
||
Let $a=5$ and $b=11$. | ||
|
||
We can use the ring formula to determine $r_a=1$ and $r_b=2$. | ||
|
||
Starting with $a$, we can calculate the 4 diagonal values on ring $r_a$: | ||
$v_3=3$ | ||
$v_5=5$ | ||
$v_7=7$ | ||
$v_9=9$ | ||
|
||
The closest of these values that is $\le{}a$ is $v_5=5$. The coordinates of that value are $v_5'=(-1\cdot{}r_a~|+1\cdot{}r_a) = (-1~|~1)$. As $v_5=a$, we don't need any further offsets. | ||
|
||
Moving on to $b$: | ||
$v_3=13$ | ||
$v_5=17$ | ||
$v_7=21$ | ||
$v_9=25$ | ||
|
||
The closest of these values that is $\le{}b$ is $v_3=13$. The coordinates of that value are $v_3'=(+1\cdot{}r_b~|+1\cdot{}r_b)=(2~|~2)$. As $v_3\neq{}b$, we still need to do more. | ||
|
||
As we determined the closest value _ahead_ of $b$ to be the top-right diagonal, we need to decrease the y coordinate of $v_3'$ by $v_3-b=13-11=2$. That leaves us with $\vec{c'}=\vec{v_3'}-\begin{pmatrix}0\\2\end{pmatrix}=\begin{pmatrix}2-0\\2-2\end{pmatrix}=\begin{pmatrix}2\\0\end{pmatrix}$. | ||
|
||
Now we know: $$a'=(-1~|~1)~~~~~~~b'=(2~|~0)$$ | ||
The connecting vector is $\vec{v}=\vec{b'}-\vec{a'}=\begin{pmatrix}2-(-1)\\0-1\end{pmatrix}=\begin{pmatrix}3\\-1\end{pmatrix}$. Applying this to $b'$ gives us the position vector of $c'$. | ||
$$\vec{c'}=\vec{b'}+\vec{v}=\begin{pmatrix}2+3\\0+(-1)\end{pmatrix}=\begin{pmatrix}5\\-1\end{pmatrix}$$ | ||
As the value is not directly on a diagonal ($|x|\neq{}|y|$), we can use the following table to determine the diagonal _ahead_ of our target value: | ||
|
||
| condition | diagonal | | ||
| ---------- | ------------ | | ||
| $\|x\|< y$ | top-left | | ||
| $x>\|y\|$ | top-right | | ||
| $x<-\|y\|$ | bottom-left | | ||
| $-\|x\|>y$ | bottom-right | | ||
|
||
We need the top-left diagonal. We also know that our value is on ring $r_c=\max(|5|, |-1|)=5$. The value of the top-left diagonal at ring 5 is, using the formula above, $v_3(5)=91$. | ||
By definition, $c$ is smaller than that value. As the straight in front of the top-left diagonal is vertical and upwards, we need to subtract the difference between the y-value of 91 and the y-value of $c'$ from 91 and we should find $c$. $$c=91-((y_{91})-(y_{c'}))=91-((+1\cdot{}5)-(-1))=91-6=85$$ | ||
|
||
That's it! $5~\lozenge{}~11=85$. | ||
|
||
## Speed | ||
|
||
This approach may seem confusing, but it avoids any loops that change with the inputs; the only loops in my code iterate over the 4 types of `Diagonal`, no more. | ||
|
||
This allows very low execution time regardless of input; results from a rough test on my computer: | ||
|
||
| `value_a` | `value_b` | time per iteration, average of 100'000'000 | | ||
| --------------- | ---------------- | ------------------------------------------ | | ||
| 1 | 2 | 529ns | | ||
| 10 | 25 | 395ns | | ||
| 100000 | 2500000 | 601ns | | ||
| 100000000000000 | 2500000000000000 | 364ns | | ||
|
||
These were just the first numbers I chose and this is in no way a proper test, but it'll do for an impression. |
Oops, something went wrong.