0 1 2 3 4 5 6 7
7 6 5 4 5 6 7 8
-.. code:: ipython2
+.. code:: ipython3
k = 4
Subtract :math:`k` from the index and take the absolute value:
-.. code:: ipython2
+.. code:: ipython3
for n in range(2 * k):
- print abs(n - k),
+ print(abs(n - k),)
.. parsed-literal::
- 4 3 2 1 0 1 2 3
+ 4
+ 3
+ 2
+ 1
+ 0
+ 1
+ 2
+ 3
Not quite. Subtract :math:`k - 1` from the index and take the absolute
value:
-.. code:: ipython2
+.. code:: ipython3
for n in range(2 * k):
- print abs(n - (k - 1)),
+ print(abs(n - (k - 1)), end=' ')
.. parsed-literal::
- 3 2 1 0 1 2 3 4
-
+ 3 2 1 0 1 2 3 4
Great, now add :math:`k`...
-.. code:: ipython2
+.. code:: ipython3
for n in range(2 * k):
- print abs(n - (k - 1)) + k,
+ print(abs(n - (k - 1)) + k, end=' ')
.. parsed-literal::
- 7 6 5 4 5 6 7 8
-
+ 7 6 5 4 5 6 7 8
So to write a function that can give us the value of a row at a given
index:
-.. code:: ipython2
+.. code:: ipython3
def row_value(k, i):
i %= (2 * k) # wrap the index at the row boundary.
return abs(i - (k - 1)) + k
-.. code:: ipython2
+.. code:: ipython3
k = 5
for i in range(2 * k):
- print row_value(k, i),
+ print(row_value(k, i), end=' ')
.. parsed-literal::
- 9 8 7 6 5 6 7 8 9 10
-
+ 9 8 7 6 5 6 7 8 9 10
(I'm leaving out details of how I figured this all out and just giving
the relevent bits. It took a little while to zero in of the aspects of
formula (but Sympy is, see below.) I'm going to write a simple Python
function to iterate and search:
-.. code:: ipython2
+.. code:: ipython3
def rank_and_offset(n):
assert n >= 2 # Guard the domain.
n -= m # Remove this rank's worth.
k += 1
-.. code:: ipython2
+.. code:: ipython3
for n in range(2, 51):
- print n, rank_and_offset(n)
+ print(n, rank_and_offset(n))
.. parsed-literal::
50 (4, 0)
-.. code:: ipython2
+.. code:: ipython3
for n in range(2, 51):
k, i = rank_and_offset(n)
- print n, row_value(k, i)
+ print(n, row_value(k, i))
.. parsed-literal::
Putting it all together
~~~~~~~~~~~~~~~~~~~~~~~
-.. code:: ipython2
+.. code:: ipython3
def row_value(k, i):
return abs(i - (k - 1)) + k
k, i = rank_and_offset(n)
return row_value(k, i)
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23)
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23000)
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23000000000000)
of an equation. For large numbers this will (eventually) be faster than
iterating as ``rank_and_offset()`` does.
-.. code:: ipython2
+.. code:: ipython3
from sympy import floor, lambdify, solve, symbols
from sympy import init_printing
init_printing()
-.. code:: ipython2
+.. code:: ipython3
k = symbols('k')
We want:
-.. code:: ipython2
+.. code:: ipython3
E = 2 + 8 * k * (k + 1) / 2 # For the reason for adding 2 see above.
.. math::
- 4 k \left(k + 1\right) + 2
+ \displaystyle 4 k \left(k + 1\right) + 2
We can write a function to solve for :math:`k` given some :math:`n`...
-.. code:: ipython2
+.. code:: ipython3
def rank_of(n):
return floor(max(solve(E - n, k))) + 1
It gives correct answers:
-.. code:: ipython2
+.. code:: ipython3
for n in (9, 10, 25, 26, 49, 50):
- print n, rank_of(n)
+ print(n, rank_of(n))
.. parsed-literal::
And it runs much faster (at least for large numbers):
-.. code:: ipython2
+.. code:: ipython3
%time rank_of(23000000000000) # Compare runtime with rank_and_offset()!
.. parsed-literal::
- CPU times: user 68 ms, sys: 8 ms, total: 76 ms
- Wall time: 73.8 ms
+ CPU times: user 27.8 ms, sys: 5 µs, total: 27.8 ms
+ Wall time: 27.3 ms
.. math::
- 2397916
+ \displaystyle 2397916
-.. code:: ipython2
+.. code:: ipython3
%time rank_and_offset(23000000000000)
.. parsed-literal::
- CPU times: user 308 ms, sys: 0 ns, total: 308 ms
- Wall time: 306 ms
+ CPU times: user 216 ms, sys: 89 µs, total: 216 ms
+ Wall time: 215 ms
.. math::
- \left ( 2397916, \quad 223606\right )
+ \displaystyle \left( 2397916, \ 223606\right)
manipulation of equations. I can put in :math:`y` (instead of a value)
and ask it to solve for :math:`k`.
-.. code:: ipython2
+.. code:: ipython3
y = symbols('y')
-.. code:: ipython2
+.. code:: ipython3
g, f = solve(E - y, k)
The equation is quadratic so there are two roots, we are interested in
the greater one...
-.. code:: ipython2
+.. code:: ipython3
g
.. math::
- - \frac{1}{2} \sqrt{y - 1} - \frac{1}{2}
+ \displaystyle - \frac{\sqrt{y - 1}}{2} - \frac{1}{2}
-.. code:: ipython2
+.. code:: ipython3
f
.. math::
- \frac{1}{2} \sqrt{y - 1} - \frac{1}{2}
+ \displaystyle \frac{\sqrt{y - 1}}{2} - \frac{1}{2}
Now we can take the ``floor()``, add 1, and ``lambdify()`` the equation
to get a Python function that calculates the rank directly.
-.. code:: ipython2
+.. code:: ipython3
floor(f) + 1
.. math::
- \lfloor{\frac{1}{2} \sqrt{y - 1} - \frac{1}{2}}\rfloor + 1
+ \displaystyle \left\lfloor{\frac{\sqrt{y - 1}}{2} - \frac{1}{2}}\right\rfloor + 1
-.. code:: ipython2
+.. code:: ipython3
F = lambdify(y, floor(f) + 1)
-.. code:: ipython2
+.. code:: ipython3
for n in (9, 10, 25, 26, 49, 50):
- print n, int(F(n))
+ print(n, int(F(n)))
.. parsed-literal::
It's pretty fast.
-.. code:: ipython2
+.. code:: ipython3
%time int(F(23000000000000)) # The clear winner.
.. parsed-literal::
- CPU times: user 0 ns, sys: 0 ns, total: 0 ns
- Wall time: 11.9 µs
+ CPU times: user 60 µs, sys: 4 µs, total: 64 µs
+ Wall time: 67 µs
.. math::
- 2397916
+ \displaystyle 2397916
Knowing the equation we could write our own function manually, but the
speed is no better.
-.. code:: ipython2
+.. code:: ipython3
from math import floor as mfloor, sqrt
def mrank_of(n):
- return int(mfloor(sqrt(23000000000000 - 1) / 2 - 0.5) + 1)
+ return int(mfloor(sqrt(n - 1) / 2 - 0.5) + 1)
-.. code:: ipython2
+.. code:: ipython3
%time mrank_of(23000000000000)
.. parsed-literal::
- CPU times: user 0 ns, sys: 0 ns, total: 0 ns
- Wall time: 12.9 µs
+ CPU times: user 7 µs, sys: 1 µs, total: 8 µs
+ Wall time: 10 µs
.. math::
- 2397916
+ \displaystyle 2397916
Now that we have a fast way to get the rank, we still need to use it to
compute the offset into a pyramid row.
-.. code:: ipython2
+.. code:: ipython3
def offset_of(n, k):
return (n - 2 + 4 * k * (k - 1)) % (2 * k)
for :math:`k` in :math:`k(k + 1)` gives :math:`(k - 1)(k - 1 + 1)`,
which of course simplifies to :math:`k(k - 1)`.)
-.. code:: ipython2
+.. code:: ipython3
offset_of(23000000000000, 2397916)
.. math::
- 223606
+ \displaystyle 223606
So, we can compute the rank, then the offset, then the row value.
-.. code:: ipython2
+.. code:: ipython3
def rank_of(n):
return int(mfloor(sqrt(n - 1) / 2 - 0.5) + 1)
i = offset_of(n, k)
return row_value(k, i)
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23)
.. math::
- 2
+ \displaystyle 2
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23000)
.. math::
- 105
+ \displaystyle 105
-.. code:: ipython2
+.. code:: ipython3
aoc20173(23000000000000)
.. math::
- 4572225
+ \displaystyle 4572225
-.. code:: ipython2
+.. code:: ipython3
%time aoc20173(23000000000000000000000000) # Fast for large values.
.. parsed-literal::
- CPU times: user 0 ns, sys: 0 ns, total: 0 ns
- Wall time: 20 µs
+ CPU times: user 22 µs, sys: 2 µs, total: 24 µs
+ Wall time: 26.7 µs
.. math::
- 2690062495969
+ \displaystyle 2690062495969
At this point I feel confident that I can implement a concise version of
this code in Joy. ;-)
-.. code:: ipython2
+.. code:: ipython3
from notebook_preamble import J, V, define
rank_of == -- sqrt 2 / 0.5 - floor ++
-.. code:: ipython2
+.. code:: ipython3
- define('rank_of == -- sqrt 2 / 0.5 - floor ++')
+ define('rank_of -- sqrt 2 / 0.5 - floor ++')
``offset_of``
~~~~~~~~~~~~~
offset_of == dup 2 * [dup -- 4 * * 2 + -] dip %
-.. code:: ipython2
+.. code:: ipython3
- define('offset_of == dup 2 * [dup -- 4 * * 2 + -] dip %')
+ define('offset_of dup 2 * [dup -- 4 * * 2 + -] dip %')
``row_value``
~~~~~~~~~~~~~
k |i-k-1| +
k+|i-k-1|
-.. code:: ipython2
+.. code:: ipython3
- define('row_value == over -- - abs +')
+ define('row_value over -- - abs +')
``aoc2017.3``
~~~~~~~~~~~~~
k i row_value
m
-.. code:: ipython2
+.. code:: ipython3
- define('aoc2017.3 == dup rank_of [offset_of] dupdip swap row_value')
+ define('aoc2017.3 dup rank_of [offset_of] dupdip swap row_value')
-.. code:: ipython2
+.. code:: ipython3
J('23 aoc2017.3')
2
-.. code:: ipython2
+.. code:: ipython3
J('23000 aoc2017.3')
105
-.. code:: ipython2
+.. code:: ipython3
V('23000000000000 aoc2017.3')
.. parsed-literal::
- . 23000000000000 aoc2017.3
- 23000000000000 . aoc2017.3
- 23000000000000 . dup rank_of [offset_of] dupdip swap row_value
- 23000000000000 23000000000000 . rank_of [offset_of] dupdip swap row_value
- 23000000000000 23000000000000 . -- sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 22999999999999 . sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 4795831.523312615 . 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 4795831.523312615 2 . / 0.5 - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 2397915.7616563076 . 0.5 - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 2397915.7616563076 0.5 . - floor ++ [offset_of] dupdip swap row_value
- 23000000000000 2397915.2616563076 . floor ++ [offset_of] dupdip swap row_value
- 23000000000000 2397915 . ++ [offset_of] dupdip swap row_value
- 23000000000000 2397916 . [offset_of] dupdip swap row_value
- 23000000000000 2397916 [offset_of] . dupdip swap row_value
- 23000000000000 2397916 . offset_of 2397916 swap row_value
- 23000000000000 2397916 . dup 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
- 23000000000000 2397916 2397916 . 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
- 23000000000000 2397916 2397916 2 . * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
- 23000000000000 2397916 4795832 . [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
- 23000000000000 2397916 4795832 [dup -- 4 * * 2 + -] . dip % 2397916 swap row_value
- 23000000000000 2397916 . dup -- 4 * * 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 2397916 2397916 . -- 4 * * 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 2397916 2397915 . 4 * * 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 2397916 2397915 4 . * * 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 2397916 9591660 . * 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 22999994980560 . 2 + - 4795832 % 2397916 swap row_value
- 23000000000000 22999994980560 2 . + - 4795832 % 2397916 swap row_value
- 23000000000000 22999994980562 . - 4795832 % 2397916 swap row_value
- 5019438 . 4795832 % 2397916 swap row_value
- 5019438 4795832 . % 2397916 swap row_value
- 223606 . 2397916 swap row_value
- 223606 2397916 . swap row_value
- 2397916 223606 . row_value
- 2397916 223606 . over -- - abs +
- 2397916 223606 2397916 . -- - abs +
- 2397916 223606 2397915 . - abs +
- 2397916 -2174309 . abs +
- 2397916 2174309 . +
- 4572225 .
+ • 23000000000000 aoc2017.3
+ 23000000000000 • aoc2017.3
+ 23000000000000 • dup rank_of [offset_of] dupdip swap row_value
+ 23000000000000 23000000000000 • rank_of [offset_of] dupdip swap row_value
+ 23000000000000 23000000000000 • -- sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 22999999999999 • sqrt 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 4795831.523312615 • 2 / 0.5 - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 4795831.523312615 2 • / 0.5 - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 2397915.7616563076 • 0.5 - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 2397915.7616563076 0.5 • - floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 2397915.2616563076 • floor ++ [offset_of] dupdip swap row_value
+ 23000000000000 2397915 • ++ [offset_of] dupdip swap row_value
+ 23000000000000 2397916 • [offset_of] dupdip swap row_value
+ 23000000000000 2397916 [offset_of] • dupdip swap row_value
+ 23000000000000 2397916 • offset_of 2397916 swap row_value
+ 23000000000000 2397916 • dup 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
+ 23000000000000 2397916 2397916 • 2 * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
+ 23000000000000 2397916 2397916 2 • * [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
+ 23000000000000 2397916 4795832 • [dup -- 4 * * 2 + -] dip % 2397916 swap row_value
+ 23000000000000 2397916 4795832 [dup -- 4 * * 2 + -] • dip % 2397916 swap row_value
+ 23000000000000 2397916 • dup -- 4 * * 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 2397916 2397916 • -- 4 * * 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 2397916 2397915 • 4 * * 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 2397916 2397915 4 • * * 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 2397916 9591660 • * 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 22999994980560 • 2 + - 4795832 % 2397916 swap row_value
+ 23000000000000 22999994980560 2 • + - 4795832 % 2397916 swap row_value
+ 23000000000000 22999994980562 • - 4795832 % 2397916 swap row_value
+ 5019438 • 4795832 % 2397916 swap row_value
+ 5019438 4795832 • % 2397916 swap row_value
+ 223606 • 2397916 swap row_value
+ 223606 2397916 • swap row_value
+ 2397916 223606 • row_value
+ 2397916 223606 • over -- - abs +
+ 2397916 223606 2397916 • -- - abs +
+ 2397916 223606 2397915 • - abs +
+ 2397916 -2174309 • abs +
+ 2397916 2174309 • +
+ 4572225 •
::