add interactive section to 4.appendix

This commit is contained in:
queue-miscreant 2025-03-02 01:09:07 -06:00
parent 9786cb0f61
commit e5946df6ec
5 changed files with 2164 additions and 36 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,8 @@ date: "2021-02-09"
date-modified: "2025-02-12"
categories:
- algebra
- python
- haskell
- interactive
execute:
eval: false
---
@ -30,7 +31,7 @@ A more unusual consequence of this is that despite the initial choice of alphabe
[negative numerals could be cleared](../#all-positive)
from the expansion by using an extra-greedy borrow.
These choices actually only differ in only one significant way: the choice of integer division function.
There is only one significant difference in how the two are implemented: the choice of integer division function.
In Haskell, there exists both `quotRem` and `divMod`, with the two disagreeing on negative remainders:
::: {.row layout-ncol="2"}
@ -43,13 +44,13 @@ divMod (-27) 5
```
:::
We can factor our choice out of our two digit-wide carry function by presenting it as an argument:
We can factor our choice out of our two digit-wide carry function by passing it as an argument:
```{haskell}
-- Widened carry of a particular repeated amount
-- i.e., for carry qr 2, the carry is 22 = 100
carry2' qr b = carry' []
where carry' zs (x:y:z:xs)
carry2 qr b = carry2' []
where carry2' zs (x:y:z:xs)
| q == 0 = carry' (x:zs) (y:z:xs) -- try carrying at a higher place value
| otherwise = foldl (flip (:)) ys zs -- carry here
where ys = r : y-x+r : z+q : xs
@ -61,8 +62,8 @@ We happen to know the root for $\langle 2, 2|$ is $\sqrt 3 + 1$, so using an app
we can check that we get a roughly constant value by evaluating at each step.
```{haskell}
{-| layout-ncol: 2 -}
{-| code-fold: true -}
-- | layout-ncol: 2
-- | code-fold: true
import IHaskell.Display
@ -74,8 +75,8 @@ pairEval x = (,) <*> hornerEval x . map fromIntegral
-- Directly expand the integer `n`, using the `carry` showing each step for `count` steps
expandSteps count carry n = take count $ iterate carry $ n:replicate count 0
cendree4QuotRem10Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 10 (carry2' quotRem 2) 4
cendree4DivMod10Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 10 (carry2' divMod 2) 4
cendree4QuotRem10Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 10 (carry2 quotRem 2) 4
cendree4DivMod10Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 10 (carry2 divMod 2) 4
markdown "`quotRem`"
markdown "`divMod`"
@ -101,14 +102,14 @@ Since the `divMod` implementation clears negative numbers from expansions, we ca
The result is another chaotic series:
```{haskell}
cendree2DivModCycleExpansion = take 11 $ iterate (carry2' divMod 2) $ take 15 $ 0:0:cycle [1,-1]
cendree2DivModCycleExpansion = take 11 $ iterate (carry2 divMod 2) $ take 15 $ 0:0:cycle [1,-1]
putStrLn . unlines . map show $ cendree2DivModCycleExpansion
```
We get the same series if we expand 2 directly (which we can also use to check its validity):
```{haskell}
cendree2DivMod15Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 15 (carry2' divMod 2) 2
cendree2DivMod15Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 15 (carry2 divMod 2) 2
putStrLn . unlines . map show $ cendree2DivMod15Steps
```
@ -116,7 +117,7 @@ We can *also* use this to get a series for negative one, a number which has a te
in the balanced alphabet.
```{haskell}
cendreeNeg1DivMod15Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 15 (carry2' divMod 2) (-1)
cendreeNeg1DivMod15Steps = map (pairEval (sqrt 3 + 1)) $ expandSteps 15 (carry2 divMod 2) (-1)
putStrLn . unlines . map show $ cendreeNeg1DivMod15Steps
```
@ -126,7 +127,7 @@ If we take the last iterate of this, increment the zeroth place value, and apply
```{haskell}
cendree0FromNeg1DivMod = (\(x:xs) -> (x + 1):xs) $ snd $ last cendreeNeg1DivMod15Steps
cendree0IncrementSteps = map (pairEval (sqrt 3 + 1)) $ take 15 $ iterate (carry2' divMod 2) cendree0FromNeg1DivMod
cendree0IncrementSteps = map (pairEval (sqrt 3 + 1)) $ take 15 $ iterate (carry2 divMod 2) cendree0FromNeg1DivMod
putStrLn . unlines . map show $ cendree0IncrementSteps
```
@ -139,27 +140,34 @@ Actually demonstrating this and proving it is left as an exercise.
Are these really -adic?
-----------------------
Perhaps it is still unconvincing that expanding the integers in this way gives something that is
Perhaps it is still unconvincing that expanding the integers in this way gives something
indeed related to *p*-adics.
The Wikipedia article on the [*p*-adic valuation](https://en.wikipedia.org/wiki/P-adic_valuation)
contains [a figure](https://commons.wikimedia.org/wiki/File:2adic12480.svg) whose description
provides a way to map *p*-adics into the complex numbers[^1].
The gist is to construct a Fourier series over truncations of numbers.
Each term of the series is weighted by a geometrically decreasing coefficient *c*.
In fact, since the expansions are in binary or (balanced) ternary, the integers should just
be a subset of the 2-adics or 3-adics.
$$
[...d_2 d_1 d_0]_p \mapsto e^{2\pi i [d_0] / p}
+ c e^{2\pi i [d_1 d_0] / p^2}
+ c^2 e^{2\pi i [d_2 d_1 d_0] / p^2}
+ ... \\
f(d; p) = \sum_{n = 0}^N c^n e^{2\pi i \cdot [d_{n:0}] / p^{n + 1}}
$$
Still, wanted to see what these numbers actually "look" like, so I whipped up an interactive diagram.
You should definitely see [this page](/interactive/adic/) for more information, but
the gist is that *p*-adics can be sent into the complex plane in a fractal-like way.
Assuming the first term dominates, one way of interpreting this is that we place numbers
around the unit circle according to their one's place.
Then, we offset each by smaller circles, each centered on the last, using more and more digits.
This produces a fractal pattern that looks like a wheel with *p* spokes.
At each point on where the spokes meet the rim, there is another wheel with *p* spokes, ad infinitum.
[^1]: Taken from the paper "Fractal geometry for images of continuous embeddings of p-adic
numbers and solenoids into Euclidean spaces" (DOI: 10.1007/BF02073866).
```{ojs}
// | echo: false
{{< include ./showAdicWithKappa.ojs >}}
```
First, notice that with $b = 2$ and $p = 2$, switching between the "b-adic" option
and the "*κ*-adic" option appears cause some points to appear and disappear.
This is easiest to see when $c \approx 0.5$.
Next, notice that when plotting the *κ*-adics, some self-similarity different from the 2- and 3-adics
can be observed for $c \approx 0.75$.
There appear to be four clusters of points, with the topmost and rightmost appearing to be
similar to one another.
Within these two clusters, the rightmost portion of them appears to be the same shape
as the larger figure.
This is actually great news -- if you switch between the *κ*-adics and the "random binary" option,
you can see that the latter option tends to the same pattern as the 2-adics.
Thus, even if the expansions for the integers are individually chaotic, together they possess a
much different structure than pure randomness.

View File

@ -0,0 +1,66 @@
/*
FileAttachments:
./cendree_DivMod_count_1024_256_digits.csv: ./cendree_DivMod_count_1024_256_digits.csv
./cendree_QuotRem_count_1024_256_digits.csv: ./cendree_QuotRem_count_1024_256_digits.csv
*/
// Import expansions from file.
// Odd numbers are injected by replacing the first entry of each row with "1"
asIntegers = (x) => {
let xs = x.split("\n").map((y) => y.split(",").map((z) => +z))
return [...xs, ...(xs.map((ys) => ys.with(0, 1)))]
};
adicExpansionsDivMod = FileAttachment(
"./cendree_DivMod_count_1024_256_digits.csv"
).text().then(asIntegers);
adicExpansionsQuotRem = FileAttachment(
"./cendree_QuotRem_count_1024_256_digits.csv"
).text().then(asIntegers);
import { expansions as oldExpansions } with { base as base } from "../../../../interactive/p-adics/showAdic.ojs";
expansionsOrAdics = baseSelector == "b-adic"
? oldExpansions
: baseSelector == "κ-adic, balanced"
? adicExpansionsDivMod
: baseSelector == "κ-adic, binary"
? adicExpansionsQuotRem
: d3.range(adicExpansionsQuotRem.length / 10).map(() => d3.range(15).map(() => +(Math.random() > 0.5)))
import { plot } with {
expansionsOrAdics as expansions,
embedBase as embedBase,
geometric as geometric,
} from "../../../../interactive/p-adics/showAdic.ojs";
viewof baseSelector = Inputs.radio([
"b-adic",
"κ-adic, balanced",
"κ-adic, binary",
"Random Binary",
], {
value: "b-adic",
label: "Expansions",
});
viewof base = Inputs.range([2, 5], {
value: 2,
step: 1,
label: "Base of expansions (b)",
disabled: baseSelector != "b-adic",
});
viewof embedBase = Inputs.range([2, 5], {
value: 2,
step: 0.1,
label: "Embedding base (p)",
});
viewof geometric = Inputs.range([0.005, 0.995], {
value: 0.9,
step: 0.005,
label: "Geometric ratio (c)",
});
plot

View File

@ -1,3 +1,5 @@
import Data.List (intercalate)
-- Widened borrow of a particular repeated amount
-- i.e., for borrow2 qr 2, the borrow is 22 = 100
-- `qr` is an integer division function returning the quotient and remainder.
@ -65,7 +67,11 @@ data QRMethod = QuotRem | DivMod deriving Show
qrMethod QuotRem = quotRem
qrMethod DivMod = divMod
cendreeEvens :: QRMethod -> Int -> Int -> IO ()
cendreeEvens qr m n = writeFile fn $ unlines $ take m $ map show $ evensQR qr' n 2 $ truncadicQR qr' (n + 2) 2 $ 2:repeat 0
cendreeEvens' :: QRMethod -> Int -> Int -> [[Int]]
cendreeEvens' qr m n = take m $ evensQR qr' n 2 $ truncadicQR qr' (n + 2) 2 $ 2:repeat 0
where qr' = qrMethod qr
fn = "cendree_" ++ show qr ++ "_count_" ++ show m ++ "_" ++ show n ++ "_digits.txt"
cendreeEvens :: QRMethod -> Int -> Int -> IO ()
cendreeEvens qr m n = writeFile fn $ display $ cendreeEvens' qr m n
where display = unlines . map (intercalate "," . map show)
fn = "cendree_" ++ show qr ++ "_count_" ++ show m ++ "_" ++ show n ++ "_digits.csv"