<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Chris Smith on Medium]]></title>
        <description><![CDATA[Stories by Chris Smith on Medium]]></description>
        <link>https://medium.com/@cdsmithus?source=rss-18bd5acaea78------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*rU9ddF_bkph8Qg3jip7vfw.jpeg</url>
            <title>Stories by Chris Smith on Medium</title>
            <link>https://medium.com/@cdsmithus?source=rss-18bd5acaea78------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 30 May 2026 14:55:18 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@cdsmithus/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Athena Loses a Bet]]></title>
            <link>https://cdsmithus.medium.com/athena-loses-a-bet-539cb2f40215?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/539cb2f40215</guid>
            <category><![CDATA[jokes]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Wed, 25 Mar 2026 18:58:04 GMT</pubDate>
            <atom:updated>2026-03-25T18:58:04.267Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*weyULcS-xuFt4Akf6f4vpA.png" /></figure><p>Athena and Ares argue over human nature, and agree to test three great minds of the age.</p><p>First, they approach Aristotle in the Lyceum and propose a bargain. “If you ask it of us, the one you love most in the world will perish, but you will be made rich beyond imagining.” Aristotle barely hesitates. “No,” he says. “To destroy the very purpose of living for the sake of the mere means is the mark of a man who lacks wisdom.”</p><p>Next, they approach Plato, finding him pacing in an olive grove of his Academy. They offer the same proposal. “I decline,” he says. “Love allows us to glimpse the ideal of pure beauty, but wealth is an anchor to the material world.”</p><p>Finally, they approach Socrates, wandering barefoot in the crowded dusty stalls of the Agora. The gods approach him with the same bargain: “If you ask it of us, Xanthippe, whom you love most in the world, will perish — ”</p><p>“I ask it!” he blurts out.</p><p>Athena blinks. “You did not even hear the rest. We were going to say you would be given wealth beyond measure.”</p><p>Socrates shrugs. “Keep it. This was never about money.”</p><p>Millenia later, Athena is still smarting from losing the bet, and she demands a rematch. Searching for another Greek philosopher, they instead find a middle aged woman writing a novel called <em>Atlas Shrugged</em>. She’s a philosopher, and Atlas was Greek, so that’s close enough.</p><p>“If you ask it,” Athena says to her, “we will make you wealthy beyond measure, but then in return, your true love will be taken from you.”</p><p>The woman looks up, bored, and asks “Why give me the money if you’re just going to take it right back?”</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=539cb2f40215" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[To Flip Or Not To Flip]]></title>
            <link>https://cdsmithus.medium.com/to-flip-or-not-to-flip-d4811e66120b?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4811e66120b</guid>
            <category><![CDATA[finance]]></category>
            <category><![CDATA[game-theory]]></category>
            <category><![CDATA[mathematics]]></category>
            <category><![CDATA[probability]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Sat, 14 Mar 2026 00:48:43 GMT</pubDate>
            <atom:updated>2026-03-16T12:00:03.913Z</atom:updated>
            <content:encoded><![CDATA[<p><em>A fair coin, an unfair offer, and the price of certainty.</em></p><p>I sat down to work out a classic probability problem numerically, and accidentally built a casino.</p><h3>The Problem of Points</h3><p>In 1654, a gambler named Antoine Gombaud posed a question to Blaise Pascal: two players are in a race to win a certain number of points. The game is interrupted. How should they divide the pot?</p><p>Pascal wrote to Fermat, and their correspondence became one of the founding documents of probability theory. The answer is elegant: if you need <em>a</em> more points and your opponent needs <em>b</em> more, you can compute the fair split with a simple recurrence. Let P(a, b) be your probability of winning:</p><ul><li>P(0, b) = 1 — you just won</li><li>P(a, 0) = 0 — your opponent just won</li><li>P(a, b) = ½ · P(a−1, b) + ½ · P(a, b−1)</li></ul><p>Every value in this table is a fraction with a power-of-2 denominator, and the numerators are just Pascal’s triangle. Beautiful math, clean solution, problem solved since the 17th century.</p><p>I built an interactive table to explore it. And then I thought: what if this were a game?</p><h3>The Game</h3><p>You and The House race to a target score. Each round, a fair coin is flipped — heads you score, tails The House scores. First to the target wins a pot of money.</p><p>But before each flip, judges look at the current game state, consult the probability table, and offer you cash to walk away. Accept, and you take the money. Decline, and the coin is flipped.</p><p>The question, every single round, is: <em>to flip or not to flip?</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/796/1*UNqw2J4ROuCLqAwivdI_kQ.png" /></figure><p>You can play at <a href="https://willowdale.online/flip">willowdale.online/flip</a>.</p><h3>How the Judges Set Their Offers</h3><p>The judges know the exact fair value of your position — they have the same formula Pascal and Fermat computed. If you have a 37.5% chance of winning a $10,000 pot, your fair value is $3,750.</p><p>But they don’t offer fair value. They offer the nearest “clean” fraction of the pot that sits strictly <em>below</em> your true odds.</p><p>“Clean” means small denominators whose only prime factors are 2, 3, and 5 — fractions like 1/3, 3/8, 7/20, nothing with a denominator above 20. These produce dollar amounts that look like something a human came up with: $3,333, $3,750, $3,500. Not $3,077 or $3,846, which look like someone ran the numbers to the last penny.</p><p>So if your fair value is $3,770 (193/512 of the pot), the judges offer $3,750 (3/8). Barely below fair, and a beautifully round number. If your fair value is $1,875 (3/16), they offer $1,666 (1/6). An 89% offer — a real discount, but still a clean, human-sounding number.</p><p>This matters psychologically. Round numbers feel like ballpark estimates — casual, generous, not fully analyzed. Precise numbers feel calculated. When the judges offer $7,500, it sounds reasonable. If they offered $7,517, you’d immediately suspect they did the math and it’s in their favor. The irony is that $7,517 is a <em>better</em> deal for you — but I think you’d be less likely to take it. The round number keeps your guard down.</p><p>The algorithm is deterministic — same game state, same offer every time. Just math dressed up in a game show contract.</p><h3>Why People Sign</h3><p>Since the offers are always strictly below fair value, the play that maximizes your expected winnings is to <em>never accept a deal</em>. The coin is fair, the game has zero house edge, and every offer leaves money on the table. A player who always flips would win 50% of their games and, on average, neither gain nor lose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/809/1*bIC-rVKVQv62gnpV455J3A.png" /></figure><p>And yet.</p><p>When you’re ahead 4–3 in a race to 10, and the contract says $6,000, and you’ve already paid $5,000 to enter this game… you hesitate. That’s a guaranteed profit. The alternative is variance — maybe you win $10,000, but you are not that far ahead. Maybe your luck turns and you lose everything.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*s4_Dy1tM1WLf2Dbo7DfVVA.png" /></figure><p>You <em>know</em> the offer is below fair. You can peek behind the curtain and see the exact numbers. The judges are shortchanging you by $128. But $128 feels like nothing when the alternative is watching your lead evaporate flip by flip.</p><p>So you sign. And $128 goes into the casino’s pocket.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/802/1*sZAWF6XdrMOb3dWu0Pxxmg.png" /></figure><p>This is what makes the game unusual. In blackjack or roulette, the house edge is baked into the rules — you can’t avoid it no matter how disciplined you are. Here, the game has no edge at all. The coin is fair. The race is symmetric. The <em>only</em> source of profit is human nature. Every dollar the casino makes is expected value that a player voluntarily left on the table.</p><p>Play for a while and you start to notice specific situations where the offer gets harder to refuse.</p><p><strong>Managing risk.</strong> A guaranteed $7,500 is safer than a coin flip worth $7,734. In real life, you might need that money for rent. Variance has a real cost, and paying a premium for certainty can be entirely rational. There is a sophisticated argument for sometimes making decisions that reduce your expected value: bankroll management, survival probability and duration. Here the stakes are fictional, your bankroll buys nothing except more fair coin flips, and going broke is solved by refreshing your web browser, so that case is weaker — but it doesn’t feel weaker when your bankroll is shrinking and the judges are holding out real-looking money.</p><p><strong>Mis-anchoring.</strong> The rational comparison is always between the offer and the expected value of continuing to flip. But that’s rarely the comparison your brain actually makes. If you were staring at a $0 offer last round and now the judges are offering $500, you’re comparing to the $0 — not to the $625 fair value. If your bankroll started at $10,000 and you’re down to $7,000, and the judges offer $3,200, you’re comparing to $10,000 — because taking the deal would put you above where you started. In both cases, the reference point that feels relevant has nothing to do with the expected value of this game.</p><p><strong>Black and white thinking.</strong> When you’re behind in the race, the most likely single outcome is that you lose. If the judges offer $500 and your odds of winning are 6%, it feels like a choice between $500 and nothing. But expected value accounts for the 6% — the rare wins are big enough to compensate for all the losses across many games. You just don’t experience many games at once. You experience this one, where you’ll probably lose, and where the person who took $500 looks smart 94 times out of 100.</p><p><strong>Imaginary momentum.</strong> You lose three flips in a row and it feels like the coin has turned against you — time to take the deal before things get worse. Or you win three in a row and feel like you’re on a streak that shouldn’t be interrupted. The coin has no memory. Each flip is independent. But the human brain is a pattern-recognition machine, and it will find narratives in random sequences whether they’re there or not.</p><h3>The Optimal Judges</h3><p>The judges in this game are clever, but simple — they mechanically pick the nearest clean fraction below fair value, blind to everything except the current expected value.</p><p>But the <em>optimal</em> offer would be very different. The right objective isn’t just the EV gap (fair value minus offer). It’s:</p><p><strong>EV gap × P(acceptance | entire game trajectory)</strong></p><p>A huge gap with low acceptance is worthless — the player just turns it down. A tiny gap with high acceptance is pennies. The sweet spot is a moderate discount the player <em>almost</em> can’t refuse.</p><p>And that acceptance probability depends on far more than just the current score — it depends on everything described above: the bankroll trajectory, the recent streak, what the last offer was, how long the player has been sitting there.</p><p>A perfect judge would think about all of this, and decide exactly what it can get you — tired, frustrated, scared little you — to accept. The clean-fraction heuristic doesn’t. And yet it still works. I still sign those offers.</p><h3>The Lesson</h3><p>The game is a playable demonstration of why casinos stay in business, maybe even why people accept below-market returns for safety, and why insurance companies are profitable.</p><p>The math is always available — right there behind a curtain. If your goal is to maximize expected dollars, the answer is always to flip the coin. And yet, round after round, the judges offer deals, and I sign them.</p><p><em>Play the game at </em><a href="https://willowdale.online/flip"><em>willowdale.online/flip</em></a><em>. It’s free, the coin is fair, and you will almost certainly take a deal you know you shouldn’t.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4811e66120b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rebooting NYHaskell]]></title>
            <link>https://cdsmithus.medium.com/rebooting-nyhaskell-84b3f8056ffa?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/84b3f8056ffa</guid>
            <category><![CDATA[haskell]]></category>
            <category><![CDATA[functional-programming]]></category>
            <category><![CDATA[programming-languages]]></category>
            <category><![CDATA[ocaml]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Mon, 22 Sep 2025 01:32:26 GMT</pubDate>
            <atom:updated>2025-10-09T00:50:29.311Z</atom:updated>
            <content:encoded><![CDATA[<p>It’s been a few years since the last meeting of the New York Haskell User Group. I’m very pleased to announce that we’ll be meeting again starting in November. Richard Eisenberg is presenting at the next meeting. I hope to see you there!</p><p><strong>A Tale of Two Lambdas: A Haskeller’s Journey Into Ocaml</strong><br><strong>November 6, 2025</strong><br><strong>Jane Street, 250 Vesey St, New York, NY 10007</strong></p><p><a href="https://www.meetup.com/ny-haskell/events/311160463">https://www.meetup.com/ny-haskell/events/311160463</a></p><p>NOTE: Please RSVP if you plan to attend. If you arrive unannounced, we’ll do our best to get you a visitor badge so you can attend, but it’s a last minute scramble for the security staff.</p><p><strong>Schedule</strong><br>6:00–6:30: Meet and Greet<br>6:30–8:30: Presentation<br>8:30–10:00: Optional Social Gathering @ a nearby bar</p><p><strong>Speaker:</strong> Richard Eisenberg</p><p>Richard Eisenberg is a Principal Researcher at Jane Street and a leading figure in the Haskell community. His work focuses on programming language design and implementation, with major contributions to GHC, including dependent types and type system extensions. He is widely recognized for advancing the expressiveness and power of Haskell’s type system while making these ideas accessible to the broader functional programming community.</p><p><strong>Abstract</strong></p><p>After spending a decade focusing mostly on Haskell, I have spent the last three years looking deeply at Ocaml. This talk will capture some lessons learned about my work in the two languages and their communities — how they are similar, how they differ, and how each might usefully grow to become more like the other. I will compare Haskell’s purity against Ocaml’s support for mutation, type classes against modules as abstraction paradigms, laziness against strictness, along with some general thoughts about language philosophy. We’ll also touch on some of the challenges both languages face as open-source products, in need of both volunteers and funding. While some functional programming experience will definitely be helpful, I’ll explain syntax as we go — no Haskell or Ocaml knowledge required, as I want this talk to be accessible equally to the two communities.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=84b3f8056ffa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Threshold Strategy in Approval and Range Voting]]></title>
            <link>https://cdsmithus.medium.com/threshold-strategy-in-approval-and-range-voting-03e59d624b72?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/03e59d624b72</guid>
            <category><![CDATA[mathematics]]></category>
            <category><![CDATA[approval-voting]]></category>
            <category><![CDATA[social-choice]]></category>
            <category><![CDATA[elections]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Tue, 20 May 2025 23:02:55 GMT</pubDate>
            <atom:updated>2025-05-28T20:25:20.638Z</atom:updated>
            <content:encoded><![CDATA[<p><em>How to turn polling insight into an optimal ballot — and why anything else is wasted.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vD48O-X6MXcdVynp0PorNg.png" /><figcaption>“approve of”? What does that mean anyway?</figcaption></figure><p>I have written previously about how <a href="https://medium.com/@cdsmithus/approval-and-score-voting-are-intrinsically-tactical-ac2ad2ffd663">approval and range voting methods are intrinsically tactical</a>. This doesn’t mean that they are <em>more</em> tactical than other election systems (nearly all of which are shown to sometimes be tactical by Gibbard’s Theorem when there are three or more options). Rather, it means that tactical voting is unavoidable. Voting in such a system requires answering the question of where to set your approval threshold or how to map your preferences to a ranged voting scale. These questions don’t have more or less “honest” answers. They are always tactical choices.</p><p>But I haven’t dug deeper into what these tactics look like. Here, I’ll do the mathematical analysis to show what effective voting looks like in these systems, and make some surprising observations along the way.</p><h4>Mathematical formalism for approval voting</h4><p>We’ll start by assuming an approval election, so the question is where to put your threshold. At what level of approval do you switch from voting not to approve a candidate to approving them?</p><p>We’ll keep the notation minimal:</p><ul><li>As is standard in probability, I’ll write ℙ[<em>X</em>] for the probability of an event <em>X</em>, and 𝔼[<em>X</em>] for the expected value of a (numerical) random variable <em>X</em>.</li><li>I will use <em>B</em> to refer to a random collection (multiset) of ballots, drawn from some probability distribution reflecting what we know from polling and other information sources on other voters. <em>B</em> will usually <em>not</em> include the approval vote that you’re considering casting, and to include that approval, we’ll write <em>B</em> ∪ {<em>c</em>}, where <em>c</em> is the candidate you contemplate approving.</li><li>I’ll write <em>W</em>(·) to indicate the <em>winner</em> of an election with a given set of ballots. This is the candidate with the most approvals. We’ll assume some tiebreaker is in place that’s independent of individual voting decisions; for instance, candidates could be shuffled into a random order before votes are cast, in in the event of a tie for number of approvals, we’ll pick the candidate who comes first in that shuffled order.</li><li><em>U</em>(·) will be your utility function, so <em>U</em>(<em>c</em>) is the utility (i.e., happiness, satisfaction, or perceived social welfare) that <em>you personally </em>will get from candidate <em>c</em> winning the election. This doesn’t mean you have to be selfish, per se, as accomplishing some altruistic goal is still a form of utility, but we evaluate that utility from your point of view even though other voters may disagree.</li></ul><p>With this notation established, we can clearly state, almost tautologically, when you should approve of a candidate <em>c</em>. You should approve of <em>c</em> whenever:</p><p>𝔼[<em>U</em>(<em>W</em>(<em>B</em> ∪ {<em>c</em>}))] &gt; 𝔼[<em>U</em>(<em>W</em>(<em>B</em>))]</p><p>That’s just saying you should approve of <em>c</em> if your expected utility from the election with your approval of <em>c</em> is more than your utility without it.</p><h4>The role of pivotal votes and exact strategy</h4><p>This inequality can be made more useful by isolating the circumstances in which your vote makes a difference in the outcome. That is, <em>W</em>(<em>B</em> ∪ {<em>c</em>}) ≠ <em>W</em>(<em>B</em>). Non-pivotal votes contribute zero to the net expectation, and can be ignored.</p><p>In approval voting, approving a candidate can only change the outcome by making that candidate the winner. This means a pivotal vote is equivalent to both of:</p><ul><li><em>W</em>(<em>B</em> ∪ {<em>c</em>}) = <em>c</em></li><li><em>W</em>(<em>B</em>) ≠ <em>c</em></li></ul><p>It’s useful to have notation for this, so we’ll define <em>V</em>(<em>B</em>, <em>c</em>) to mean that <em>W</em>(<em>B</em> ∪ {<em>c</em>}) ≠ <em>W</em>(<em>B</em>), or equivalently, that <em>W</em>(<em>B</em> ∪ {<em>c</em>}) = <em>c</em> and <em>W</em>(<em>B</em>) ≠ <em>c</em>. To remember this notation, recall that V is the pivotal letter in the word “pivot”, and also visually resembles a pivot.</p><p>With this in mind, the expected gain in utility from approving <em>c</em> is:</p><ul><li>𝔼[<em>U</em>(<em>W</em>(<em>B</em> ∪ {<em>c</em>}))] - 𝔼[<em>U</em>(<em>W</em>(<em>B</em>))]. But since the utility gain is zero except for pivotal votes, this is the same as</li><li>ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] · (𝔼[<em>U</em>(<em>W</em>(<em>B</em> ∪ {<em>c</em>})) | <em>V</em>(<em>B,</em> <em>c</em>)] - 𝔼[<em>U</em>(<em>W</em>(<em>B</em>)) | <em>V</em>(<em>B,</em> <em>c</em>)]). But since <em>V</em>(<em>B,</em> <em>c</em>) implies that <em>W</em>(<em>B</em> ∪ {<em>c</em>}) = <em>c</em>, so this simplifies to</li><li>ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] · (<em>U</em>(<em>c</em>) - 𝔼[<em>U</em>(<em>W</em>(<em>B</em>)) | <em>V</em>(<em>B,</em> <em>c</em>)])</li></ul><p>Therefore, you ought to approve of a candidate <em>c</em> whenever</p><p>U(<em>c</em>) &gt; 𝔼[<em>U</em>(<em>W</em>(<em>B</em>)) | <em>V</em>(<em>B,</em> <em>c</em>)]</p><p>This is much easier to interpret. You should approve of a candidate <em>c</em> precisely when the utility you obtain from <em>c</em> winning is greater than the expected utility in cases where <em>c</em> is right on the verge of winning (but someone else wins instead).</p><p>There are a few observations worth making about this:</p><ul><li>The expectation clarifies why the threshold setting part of approval voting is intrinsically tactical. It involves evaluating how likely each other candidate is to win, and using that information to compute an expectation. That means advice to vote only based on <em>internal</em> feelings like whether you consider a candidate acceptable is always wrong. An effective vote takes into account <em>external</em> information about how others are likely to vote, including polling and understanding of public opinion and mood.</li><li>The conditional expectation, assuming <em>V</em>(<em>B</em>, <em>c</em>), tells us that the optimal strategy for whether to approve of some candidate <em>c</em> depends on the very specific situation where <em>c</em> is right on the verge of winning the election. If <em>c</em> is a frontrunner in the election, this scenario isn’t likely to be too different from the general case, and the conditional probability doesn’t change much. However, if <em>c</em> is a long-shot candidate from some minor party, but somehow nearly ties for a win, we’re in a strange situation indeed: perhaps a major last-minute scandal, a drastic polling error, or a fundamental misunderstanding of the public mood. Here, the conditonal expected utility of an alternate winner might be quite different from your unconditional expectation. If, say, voters prove to have an unexpected appetite for extremism, this can affect the runner-ups, as well.</li><li>Counter-intuitively, an optimal strategy might even involve approving some candidates that you like <em>less</em> than some that you don’t approve! This can happen because different candidates are evaluated against different thresholds. Therefore, a single voter’s best approval ballot isn’t necessarily <em>monotonic</em> in their utility rankings. This adds a level of strategic complexity I hadn’t anticipated in my earlier writings on strategy in approval voting.</li></ul><h4>Approximate strategy</h4><p>The strategy described above is rigorously optimal, but not at all easy to apply. Imagining the bizarre scenarios in which each candidate, no matter how minor, might tie for a win, is challenging to do well. We’re fortunate, then, that there’s a good approximation. Remember that the utility gain from approving a candidate was equal to</p><p>ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] · (<em>U</em>(<em>c</em>) - 𝔼[<em>U</em>(<em>W</em>(<em>B</em>)) | <em>V</em>(<em>B,</em> <em>c</em>)])</p><p>In precisely the cases where <em>V</em>(<em>B</em>, <em>c</em>) is a bizarre assumption that’s difficult to imagine, we’re also multiplying by ℙ[<em>V</em>(<em>B,</em> <em>c</em>)], which is vanishingly small, so this vote is very unlikely to make a difference in the outcome. For front-runners, who are relatively much more likely to be in a tie for the win, the conditional probability changes a lot less: scenarios that end in a near-tie are not too different from the baseline expectation.</p><p>This happens because ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] falls off quite quickly indeed as the popularity of <em>c</em> decreases, especially for large numbers of voters. For a national scale election (say, about 10 million voters), if <em>c</em> expects around 45% of approvals, then ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] is around one in a million. That’s a small number, telling us that very large elections aren’t likely to be decided by a one-vote margin anyway. But it’s gargantuan compared to the number if <em>c</em> expects only 5% of approvals. Then ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] is around one in 10^70. That’s about one in a quadrillion-vigintillion, if you want to know, and near the scale of possibly picking one atom at random from the entire universe! The probability of casting a pivotal vote drops off exponentially, and by this point it’s effectively zero.</p><p>With that in mind, we can drop the condition on the probability in the second term, giving us a new rule: Approve of a candidate <em>c</em> any time that:</p><p><em>U</em>(<em>c</em>) &gt; 𝔼[<em>U</em>(<em>W</em>(<em>B</em>))]</p><p>That is, approve of any candidate whose win you would like better than you <em>expect</em> to like the outcome of the election. In other words, imagine you have no other information on election night, and hear that this candidate has won. If this would be good news, approve of the candidate on your ballot. If it would be bad news, don’t.</p><ul><li>This rule is still tactical. To determine how much you expect to like the outcome of the election, you need to have beliefs about who else is likely to win, which still requires an understanding of polling and public opinion and mood.</li><li>However, there is <em>one</em> threshold, derived from real polling data in realistic scenarios, and you can cast your approval ballot monotonically based on that single threshold.</li></ul><p>This is no longer a true optimal strategy, but with enough voters, the exponential falloff in ℙ[<em>V</em>(<em>B,</em> <em>c</em>)] as <em>c</em> becomes less popular is a pretty good assurance that the incorrect votes you might cast by using this strategy instead of the optimal ones are extremely unlikely to matter. In practice, this is probably the best rule to communicate to voters in an approval election with moderate to large numbers of voters.</p><p>We can get closer with the following hypothetical: Imagine that on election night, you have no information on the results <em>except</em> for a headline that proclaims: <strong>Election Too Close To Call</strong>. With <em>that</em> as your prior, you ask of each candidate, is it good or bad news to hear <em>now</em> that this candidate has won. If it would be good news, then you approve of them. This still leaves one threshold, but we’re no longer making the leap that the pivotal condition for front-runners is unnecessary; we’re imagining a world in which at least <em>some candidates</em>, almost surely the front-runners, are tied. If this changes your decision (which it likely would only in very marginal cases), you can use this more accurate approximation.</p><h4>Reducing range to approval voting</h4><p>I promised to look at strategy for range voting, as well. Armed with an appreciation of approval strategy, it’s easy to extend this to an optimal range strategy, as well, for large-scale elections.</p><p>The key is to recognize that a range voting election with options 0, 1, 2, …, <em>n</em> is mathematically equivalent to an approval election where everyone is just allowed to vote <em>n</em> times. The number you mark on the range ballot can be interpreted as saying how many of your approval ballots you want to mark as approving that candidate.</p><p>Looking at it this way presents the obvious question: why would you vote differently on some ballots than others? In what situation could that possibly be the right choice?</p><ul><li>For small elections, say if you’re voting on places to go out and eat with your friends or coworkers, it’s possible that adding in a handful of approvals materially changes the election so that the optimal vote is different. Then it may well be optimal to cast a range ballot using some intermediate number.</li><li>For large elections, though, you’re presented with pretty much exactly the same question each time, and you may as well give the same answer. Therefore, in large-scale elections, the optimal way to vote with a range ballot is always to rate everyone either the minimum or maximum possible score. This reduces a range election exactly to an approval election. The additional expressiveness of a range ballot is a siren call: by using it, you always vote less effectively than you would have by ignoring it and using only the two extreme choices.</li></ul><p>Since we’re discussing political elections, which have relatively large numbers of voters, this answers the question for range elections, as well: Rate a candidate the maximum score if you like them better than you <em>expect</em> to like the outcome of the election. Otherwise, rate them the minimum score.</p><h4>Summing it up</h4><p>What we’ve learned, then, is that optimal voting in approval or range systems boils down to two nested rules.</p><ul><li><strong>Exact rule (for the mathematically fearless):</strong> approve <em>c</em> iff <em>U</em>(<em>c</em>) &gt; 𝔼[ <em>U</em>(<em>W</em>(<em>B</em>)) | your extra vote for <em>c</em> is pivotal ]. This Bayesian test weighs each candidate against the expected utility in the razor-thin worlds where they tie for first.</li><li><strong>Large-electorate shortcut (for everyone else):</strong> because those pivotal worlds become astronomically rare as the field grows, the condition shrinks to a single cutoff: approve (or give a maximum score) to every candidate whose victory you expect to enjoy more than you <em>expected</em> to like the result. (If you can, imagine only cases where you know the election is close.)</li></ul><p>We’ve seen why the first rule is the gold standard; but the second captures virtually all of its benefit when millions are voting. Either way, strategy is inseparable from sincerity: you must translate beliefs about polling into a utility threshold, and then measure every candidate against it. We’ve also seen by a clear mathematical equivalence why range ballots add no real leverage in large-scale elections, instead only offering false choices that are always wrong.</p><p>The entire playbook fits on a sticky note: compute the threshold, vote all-or-nothing, and let the math do the rest.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=03e59d624b72" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[When is a call stack not a call stack?]]></title>
            <link>https://cdsmithus.medium.com/when-is-a-call-stack-not-a-call-stack-f7f12d7aabbe?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/f7f12d7aabbe</guid>
            <category><![CDATA[haskell]]></category>
            <category><![CDATA[codeworld]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Tue, 10 Dec 2024 22:06:21 GMT</pubDate>
            <atom:updated>2024-12-10T22:50:40.245Z</atom:updated>
            <content:encoded><![CDATA[<p>Tom Ellis, who I have the privilege of working with at <a href="http://groq.com">Groq</a>, has an <a href="https://h2.jaguarpaw.co.uk/posts/hascallstack-domain-errors/">excellent article</a> up about using HasCallStack in embedded DSLs. You should read it. If you don’t, though, the key idea is that HasCallStack isn’t just about exceptions: you can use it to get source code locations in many different contexts, and storing call stacks with data is <em>particularly</em> powerful in providing a helpful experience to programmers.</p><p>Seeing Tom’s article reminded me of a CodeWorld feature which was implemented long ago, but I’m excited to share again in this brief note.</p><h4>CodeWorld Recap</h4><p>If you’re not familiar with CodeWorld, it’s a web-based programming environment I created mainly to teach mathematics and computational thinking to students in U.S. middle school, ages around 11 to 14 years old. The programming language is based on Haskell — well, it is technically Haskell, but with a lot of preprocessing and tricks aimed at smoothing out the rough edges. There’s also a pure Haskell mode, giving you the full power of the idiomatic Haskell language.</p><p>In CodeWorld, the standard library includes primitives for putting pictures on the screen. This includes:</p><ul><li>A few primitive pictures: circles, rectangles, and the like</li><li>Transformations to rotate, translate, scale, clip, and and recolor an image</li><li>Compositions to overlay and combine multiple pictures into a more complex picture.</li></ul><p>Because the environment is functional and declarative — and this will be important — there isn’t a primitive to <em>draw</em> a circle. There is a primitive that <em>represents</em> the concept of a circle. You can include a circle in your drawing, of course, but you compose a picture by combining simpler pictures declaratively, and then draw the whole thing only at the very end.</p><h4>Debugging in CodeWorld</h4><p>CodeWorld’s declarative interface enables a number of really fun kinds of interactivity… what programmers might call “debugging”, but for my younger audience, I view as exploratory tools: ways they can pry open the lid of their program and explore what it’s doing.</p><p>There are a few of these that are pretty awesome. Lest I seem to be claiming the credit, the implementation for these features is due to two students in Summer of Haskell and then in Google Summer of Code: Eric Roberts, and Krystal Maughan.</p><ul><li>Not the point here, but there are some neat features for rewinding and replaying programs, zooming in, etc.</li><li>There’s also an “inspect” mode, in which you not only see the final result, but the whole <em>structure</em> of the resulting picture (e.g., maybe it’s an overlay of three other pictures: a background, and two characters, and each of those is transformed in some way, and the base picture for the transformation is some other overlay of multiple parts…) This is possible because pictures are represented not as bitmaps, but as data structures that remember how the picture was built from its individual parts</li></ul><p>Krystal’s <a href="https://medium.com/@krystal.maughan/breaking-the-space-time-barrier-with-haskell-time-traveling-and-debugging-in-codeworld-a-google-e87894dd43d7">recap blog post</a> contains demonstrations of not only her own contributions, but the inspect window as well. Here’s a section showing what I’ll talk about now.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FKETt7zzMy-M%3Fstart%3D37%26feature%3Doembed%26start%3D37&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DKETt7zzMy-M&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FKETt7zzMy-M%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7f09408e8411d852516bedb5aab2601c/href">https://medium.com/media/7f09408e8411d852516bedb5aab2601c/href</a></iframe><p>The inspect window is linked to the code editor! Hover over a structural part of the picture, and you can see which expression in your own code produced that part of the picture.</p><p>This is another application of the technique from Tom’s post. The data type representing pictures in CodeWorld stores a call stack captured at each part of the picture, so that when you inspect the picture and hover over some part, the environment knows where in your code you described that part, and it highlights the code for you, and jumps there when clicked.</p><p>While it’s the same technique, I really like this example because it’s not at all like an exception. We aren’t reporting errors or anything of the sort. Just using this nice feature of GHC that makes the connection between code and declarative data observable to help our users observe things about their own code.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f7f12d7aabbe" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Playing With a Game]]></title>
            <link>https://cdsmithus.medium.com/playing-with-a-game-0dd944082e2c?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/0dd944082e2c</guid>
            <category><![CDATA[game-theory]]></category>
            <category><![CDATA[mathematics]]></category>
            <category><![CDATA[haskell]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Mon, 23 Sep 2024 17:30:58 GMT</pubDate>
            <atom:updated>2024-09-27T07:19:43.644Z</atom:updated>
            <content:encoded><![CDATA[<p>In a recent comment (that I sadly cannot find any longer) in <a href="https://www.reddit.com/r/math/">https://www.reddit.com/r/math/</a>, someone mentioned the following game. There are <em>n</em> players, and they each independently choose a natural number. The player with the lowest unique number wins the game. So if two people choose 1, a third chooses 2, and a fourth chooses 5, then the third player wins: the 1s were not unique, so 2 was the least among the unique numbers chosen. (Presumably, though this wasn’t specified in the comment, if there is no unique number among all players, then no one wins).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*93BSmqgIZJXoX--aRhGvBg.png" /></figure><p>I got nerd-sniped, so I’ll share my investigation.</p><p>For me, since the solution to the general problem wasn’t obvious, it made sense to specialize. Let’s say there are <em>n</em> players, and just to make the game finite, let’s say that instead of choosing <em>any</em> natural number, you choose a number from 1 to <em>m</em>. Choosing very large numbers is surely a bad strategy anyway, so intuitively I expect any reasonably large choice of <em>m</em> to give very similar results.</p><h4><em>n</em> = 2</h4><p>Let’s start with the case where <em>n</em> = 2. This one turns out to be easy: you should always pick 1, daring your opponent to pick 1, as well. We can induct on <em>m</em> to prove this. If <em>m</em> = 1, then you are required to pick 1 by the rules. But if <em>m</em> &gt; 1, suppose you pick <em>m</em>. Either your opponent also picks <em>m</em> and you both lose, or your opponent picks a number smaller than <em>m</em> and you still lose. Clearly, this is a bad strategy, and you always do at least as well choosing one of the first <em>m - </em>1 options instead. This reduces the game to one where we already know the best strategy is to pick 1.</p><p>That wasn’t very interesting, so let’s try more players.</p><h4>n = 3, m = 2</h4><p>Suppose there are three players, each choosing either 1 or 2. It’s impossible for all three players to choose a different number! If you do manage to pick a unique number, then, you will be the only player to do so, so it will always be the <em>least</em> unique number simply because it’s the <em>only</em> one!</p><p>If you don’t think your opponents will have figured this out, you might be tempted to pick 2, in hopes that your opponents go for 1 to try to get the least number, and you’ll be the only one choosing 2. But this makes you predictable, so the other players can try to take advantage. But if one of the other players reasons the same way, you both are guaranteed to lose! What we want here is a Nash equilibrium: a strategy for all players such that no single player can do better by deviating from that strategy.</p><p>It’s not hard to see that all players should flip a coin, choosing either 1 or 2 with equal probability. There’s a 25% chance each that a player picks the unique number and wins, and there’s a 25% chance that they all choose the same number and all lose. Regrettable, but anything you do to try to avoid that outcome just makes your play more predictable so that the other players could exploit that.</p><p>It’s interesting to look at the actual computation. When computing a Nash equilibrium, we generally rely on the indifference principle: a player should always be indifferent between any choice that they make at random, since otherwise, they would take the one with the better outcome and always play that instead.</p><p>This is a bit counter-intuitive! Naively, you might think that the optimal strategy is the one that gives the best expected result, but when a Nash equilibrium involves a random choice— known as a <em>mixed</em> strategy — then any single player actually does equally well against other optimal players no matter which mix of those random choices they make! In this game, though, predictability is a weakness. Just as a poker player tries to avoid ‘tells’ that give away the strength of their hand, players in this number-choosing game need to be unpredictable. The reason for playing the Nash equilibrium <em>isn’t</em> that it gives the best expected result against optimal opponents, but rather that it can’t be <em>exploited</em> by an opponent.</p><p>Let’s apply this indifference principle. This game is completely symmetric — there’s no order of turns, and all players have the same choices and payoffs available — so an optimal strategy ought to be the same for any player. Then, let’s say <em>p</em> is the probability that any single player will choose 1. Then if <em>you</em> choose 1, you will win with probability (1 — <em>p</em>)², while if you choose 2, you’ll win with probability <em>p</em>². If you set these equal to each other as per the indifference principle, and solve the equation, you get <em>p</em> = 0.5, as we reasoned above.</p><h4>n = 3, m = 3</h4><p>Things get more interesting if each player can choose 1, 2, or 3. Now it’s possible for each player to choose uniquely, so it starts to matter <em>which</em> unique number you pick. Let’s say each player chooses 1, 2, and 3 with the probabilities <em>p</em>, <em>q</em>, and <em>r</em> respectively. We can analyze the probability of winning with each choice.</p><ul><li>If you pick 1, then you always win <em>unless</em> someone else also picks a 1. Your chance of winning, then, is (<em>q</em> + <em>r</em>)².</li><li>If you pick 2, then for you to win, either <em>both</em> other players need to pick 1 (eliminating each other because of uniqueness and leaving you to win by default), or <em>both</em> other players need to pick 3, so that you’ve picked the least number. Your chance of winning is <em>p</em>²<em> </em>+<em> r</em>².</li><li>If you pick 3, then you need your opponents to pick the same different number: either 1 or 2. Your chance of winning is <em>p</em>²<em> </em>+<em> q</em>².</li></ul><p>Setting these equal to each other immediately shows us that since <em>p</em>²<em> </em>+<em> q</em>² = <em>p</em>²<em> </em>+<em> r</em>², we must conclude that <em>q</em> = <em>r.</em> Then <em>p</em>²<em> </em>+<em> q</em>² = (<em>q</em> + <em>r</em>)² = 4<em>q</em>², so <em>p</em>² = 3<em>q</em>² = 3<em>r</em>². Together with <em>p</em> + <em>q</em> + <em>r</em> = 1, we can conclude that <em>p</em> = 2√3 - 3 ≈ 0.464, while <em>q</em> = <em>r</em> = 2 - √3 ≈ 0.268.</p><p>This is our first really interesting result. Can we generalize?</p><h4>n = 3, in general</h4><p>The reasoning above generalizes well. If there are three players, and you pick a number <em>k</em>, you are betting that either the other two players will pick the same number less than <em>k</em>, or they will each pick numbers greater than <em>k</em> (regardless of whether they are the same one).</p><p>I’ll switch notation here for convenience. Let <em>X</em> be a random variable representing a choice by a player from the Nash equilibrium strategy. Then if you choose <em>k</em>, your probability of winning is P(<em>X</em>=1)² + … + P(<em>X</em>=<em>k</em>-1)² + P(<em>X&gt;k</em>)². The indifference principle tells us that this should be equal for any choice of <em>k</em>. Equivalently, for any <em>k</em> from 1 to <em>m </em>- 1, the probability of winning when choosing <em>k</em> is the same as the probability when choosing <em>k</em> + 1. So:</p><ul><li>P(<em>X</em>=1)² + … + P(<em>X</em>=<em>k</em>-1)² + P(<em>X&gt;k</em>)² = P(<em>X</em>=1)² + … + P(<em>X</em>=<em>k</em>)² + P(<em>X&gt;k</em>+1)²</li><li>Cancelling the common terms: P(<em>X&gt;k</em>)² = P(<em>X</em>=<em>k</em>)² + P(<em>X&gt;k</em>+1)²</li><li>Rearranging: P(<em>X</em>=<em>k</em>) = √(P(<em>X≥k</em>+1)² - P(<em>X&gt;k</em>+1)²)</li></ul><p>This gives us a recursive formula that we can use (in reverse) to compute P(<em>X</em>=<em>k</em>), if only we knew P(<em>X</em>=<em>m</em>) to get started. If we just pick something arbitrary, though, it turns out that all the results are just multiples of that choice. We can then divide by the sum of them all to normalize the probabilities to sum to 1.</p><p>Here I can write some code (in Haskell):</p><pre>import Probability.Distribution (Distribution, categorical, probabilities)<br><br>nashEquilibriumTo :: Integer -&gt; Distribution Double Integer<br>nashEquilibriumTo m = categorical (zip allPs [1 ..])<br>  where<br>    allPs = go m 1 0 []<br>    go 1 pEqual pGreater ps = (/ (pEqual + pGreater)) &lt;$&gt; (pEqual : ps)<br>    go k pEqual pGreater ps =<br>      let pGreaterEqual = pEqual + pGreater<br>       in go<br>            (k - 1)<br>            (sqrt (pGreaterEqual * pGreaterEqual - pGreater * pGreater))<br>            pGreaterEqual<br>            (pEqual : ps)<br><br>main :: IO ()<br>main = print (probabilities (nashEquilibriumTo 100))</pre><p>I’ve used a probability library from <a href="https://github.com/cdsmith/prob">https://github.com/cdsmith/prob</a> that I wrote with Shae Erisson during a fun hacking session a few years ago. It doesn’t help yet, but we’ll play around with some of its further features below.</p><p>Trying a few large values for <em>m</em> confirms my suspicion that any reasonably large choice of <em>m</em> gives effectively the same result.</p><pre>1 -&gt; 0.4563109873079237<br>2 -&gt; 0.24809127016999155<br>3 -&gt; 0.1348844977362459<br>4 -&gt; 7.333521940168612e-2<br>5 -&gt; 3.987155303205954e-2<br>6 -&gt; 2.1677725302500214e-2<br>7 -&gt; 1.1785941067126387e-2</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Stm7uB6P1fkxmyB1RpIsYg.png" /></figure><p>By inspection, this appears to be a geometric distribution, parameterized by the probability 0.4563109873079237. We can check that the distribution is geometric, which just means that for all <em>k</em> &lt; <em>m</em> - 1, the ratio P(<em>X &gt; k</em>) / P(<em>X </em>≥ <em>k</em>) is the same as P(<em>X &gt; k </em>+ 1) / P(<em>X</em> ≥ <em>k</em> + 1). This is the defining property of a geometric distribution, and some simple algebra confirms that it holds in this case.</p><p>But what is this bizarre number? A few Google queries gets us to an answer of sorts. A <a href="https://www.polyomino.org.uk/publications/2003/dissertation.pdf">2002 Ph.D. dissertation by Joseph Myers</a> seems to arrive at the same number in the solution to a question about graph theory, where it’s identified as the real root of the polynomial <em>x</em>³ - 4<em>x</em>² + 6<em>x </em>- 2. We can check that this is right for a geometric distribution. Starting with P(<em>X</em>=<em>k</em>) = √(P(<em>X≥k</em>+1)² -P(<em>X&gt;k</em>+1)²) where <em>k </em>= 1, we get P(<em>X</em>=1) = √(P(<em>X ≥ </em>2)² -P(<em>X &gt; </em>2)²). If P(<em>X</em>=1) = <em>p</em>, then P(<em>X ≥ </em>2) = 1 - <em>p</em>, and P(<em>X &gt; </em>2) = (1 - <em>p</em>)², so we have <em>p</em> = √((1-<em>p</em>)² - ((1 - <em>p)</em>²)²), which indeed expands to <em>p</em>⁴ - 4<em>p</em>³ + 6<em>p</em>² - 2<em>p</em> = 0, so either <em>p</em> = 0 (which is impossible for a geometric distribution), or <em>p</em>³ - 4<em>p</em>² + 6<em>p</em> - 2 = 0, giving the probability seen above. (How and if this is connected to the graph theory question investigated in that dissertation, though, is certainly beyond my comprehension.)</p><p>You may wonder, in these large limiting cases, how often it turns out that no one wins, or that we see wins with each number. Answering questions like this is why I chose to use my probability library. We can first define a function to implement the game’s basic rule:</p><pre>leastUnique :: (Ord a) =&gt; [a] -&gt; Maybe a<br>leastUnique xs = listToMaybe [x | [x] &lt;- group (sort xs)]</pre><p>And then we can define the whole game using the strategy above for each player:</p><pre>gameTo :: Integer -&gt; Distribution Double (Maybe Integer)<br>gameTo m = do<br>  ns &lt;- replicateM 3 (nashEquilibriumTo m)<br>  return (leastUnique ns)</pre><p>Then we can update main to tell us the distribution of game outcomes, rather than plays:</p><pre>main :: IO ()<br>main = print (probabilities (gameTo 100))</pre><p>And get these probabilities:</p><pre>Nothing -&gt; 0.11320677243374572<br>Just 1  -&gt; 0.40465349320873445<br>Just 2  -&gt; 0.22000565820506113<br>Just 3  -&gt; 0.11961465909617276<br>Just 4  -&gt; 6.503317590749513e-2<br>Just 5  -&gt; 3.535782320137907e-2<br>Just 6  -&gt; 1.9223659987298684e-2<br>Just 7  -&gt; 1.0451692718822408e-2</pre><p>An 11% probability of no winner for large <em>m</em> is an improvement over the 25% we computed for <em>m</em> = 2. Once again, a least unique number greater than 7 has less than 1% probability, and the probabilities drop even more rapidly from there.</p><h4>More than three players?</h4><p>With an arbitrary number of players, the expressions for the probability of winning grow rather more involved, since you must consider the possibility that <em>some</em> other players have chosen numbers greater than yours, while <em>others </em>have chosen smaller numbers that are duplicated, possibly in twos or in threes.</p><p>For the four-player case, this isn’t too bad. The three winning possibilities are:</p><ul><li>All three other players choose the same smaller number. This has probability P(<em>X</em>=1)³ + … + P(<em>X</em>=<em>k</em>-1)³</li><li>All three other players choose larger numbers, though not necessarily the same one. This has probability P(<em>X </em>&gt; <em>k</em>)³</li><li>Two of the three other players choose the same smaller number, and the third chooses a larger number. This has probability 3 P(<em>X </em>&gt; <em>k</em>) (P(<em>X</em>=1)² + … + P(<em>X</em>=<em>k</em>-1)²)</li></ul><p>You could possibly work out how to compute this one without too much difficulty. The algebra gets harder, though, and I dug deep enough to determine that the Nash equilibrium is no longer a geometric distribution. If you assume the Nash equilibrium is geometric, then numerically, the probability of choosing 1 that gives 1 and 2 equal rewards would need to be about 0.350788, but this choice gives too small a reward for choosing 3 or more, implying they ought to be chosen less often.</p><p>For larger <em>n</em>, even stating the equations turns into a nontrivial problem of accurately counting the possible ways to win. I’d certainly be interested if there’s a nice-looking result here, but I do not yet know what it is.</p><h4>Numerical solutions</h4><p>We can solve this numerically, though. Using the probability library mentioned above, one can easily compute, for any finite game and any strategy (as a probability distribution of moves) the expected benefit for each choice.</p><pre>expectedOutcomesTo :: Int -&gt; Int -&gt; Distribution Double Int -&gt; [Double]<br>expectedOutcomesTo n m dist =<br>  [ probability (== Just i) $ leastUnique . (i :) &lt;$&gt; replicateM (n - 1) dist<br>    | i &lt;- [1 .. m]<br>  ]</pre><p>We can then then iteratively adjust the probability of each choice slightly based on how its expected outcome compares to other expected outcomes in the distribution. It turns out to be good enough to compare with an immediate neighbor. Just so that all of our distributions remain valid, instead of working with the global probabilities P(<em>X</em>=<em>k</em>), we’ll do the computation with conditional probabilities P(<em>X </em>= <em>k </em>| <em>X </em>≥ <em>k</em>), so that any sequence of probabilities is valid, without worrying about whether they sum to 1. Given this list of conditional probabilities, we can produce a probability distribution like this.</p><pre>distFromConditionalStrategy :: [Double] -&gt; Distribution Double Int<br>distFromConditionalStrategy = go 1<br>  where<br>    go i [] = pure i<br>    go i (q : qs) = do<br>      choice &lt;- bernoulli q<br>      if choice then pure i else go (i + 1) qs</pre><p>Then we can optimize numerically, using the difference of each choice’s win probability from its neighbor as a diff to add to the conditional probability of that choice.</p><pre>refine :: Int -&gt; Int -&gt; [Double] -&gt; Distribution Double Int<br>refine n iters strategy<br>  | iters == 0 = equilibrium<br>  | otherwise =<br>      let ps = expectedOutcomesTo n m equilibrium<br>          delta = zipWith subtract (drop 1 ps) ps<br>          adjs = zipWith (+) strategy delta<br>       in refine n (iters - 1) adjs<br>  where<br>    m = length strategy + 1<br>    equilibrium = distFromConditionalStrategy strategy</pre><p>It works well enough to run this for 10,000 iterations at <em>n</em> = 4, <em>m</em> = 10.</p><pre>main :: IO ()<br>main = do<br>  let n = 4<br>      m = 10<br>      d = refine n 10000 (replicate (m - 1) 0.3)<br>  print $ probabilities d<br>  print $ expectedOutcomesTo n m d</pre><p>The resulting probability distribution is, to me, at least, quite surprising! I would have expected that more players would incentivize you to choose a higher number, since the additional players make collisions on low numbers more likely. But it seems the opposite is true. While three players at least occasionally (with 1% or more probability) should choose numbers up to 7, four players should apparently stop at 3.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*fNurIb39kCDfvs4jPXRu_A.png" /><figcaption>Nash equilibrium strategy for n = 4, m = 10</figcaption></figure><p>Huh. I’m not sure why this is true, but I’ve checked the computation in a few ways, and it seems to be a real phenomenon. Please leave a comment if you have a better intuition for why it ought to be so!</p><p>With five players, at least, we see some larger numbers again in the Nash equilibrium, lending support to the idea that there was something unusual going on with the four player case. Here’s the strategy for five players:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*iuQsMMvaNV-UPO3zQO5i-Q.png" /><figcaption>Nash equilibrium strategy for n = 5, m = 10</figcaption></figure><p>The six player variant retracts the distribution a little, reducing the probabilities of choosing 5 or 6, but then 7 players expands the choices a bit, and it’s starting to become a pattern that even numbers of players lend themselves to a tighter style of play, while odd numbers open up the strategy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*qLRzIXG4Ad1Wv9c8jDBSVg.png" /><figcaption>Nash equilibrium strategy for n = 6, m = 10</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*vgnYFwNsj2u9dzne2NF_HA.png" /><figcaption>Nash equilibrium strategy for n = 7, m = 10</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*ec1PwSgpwsOENq9wCX9eEw.png" /><figcaption>Nash equilibrium strategy for n = 8, m = 10</figcaption></figure><p>In general, it looks like this is converging to something. The computations are also getting progressively slower, so let’s stop there.</p><h4>Game variants</h4><p>There is plenty of room for variation in the game, which would change the analysis. If you’re looking for a variant to explore on your own, in addition to expanding the game to more players, you might try these:</p><ul><li>What if a tie awards each player an equal fraction of the reward for a full win, instead of nothing at all? (This actually simplifies the analysis a bit!)</li><li>What if, instead of all wins being equal, we found the least unique number, and paid that player an amount equal to the number itself? Now there’s somewhat less of an incentive for players to choose small numbers, since a larger number gives a large payoff! This gives the problem something like a prisoner’s dilemma flavor, where players could coordinate to make more money, but leave themselves open to being undercut by someone willing to make a small profit by betraying the coordinated strategy.</li></ul><p>What other variants might be interesting?</p><h4>Addendum (Sep 26): Making it faster</h4><p>As is often the case, the naive code I originally wrote can be significantly improved. In this case, the code was evaluating probabilities by enumerating all the ways players might choose numbers, and then computing the winner for each one. For large values of <em>m</em> and <em>n</em> this is a lot, and it grows exponentially.</p><p>There’s a better way. We don’t need to remember each individual choice to determine the outcome of the game in the presence of further choices. Instead, we need only determine which numbers have been chosen once, and which have been chosen more than once.</p><pre>data GameState = GameState<br>  { dups :: Set Int,<br>    uniqs :: Set Int<br>  }<br>  deriving (Eq, Ord)</pre><p>To add a new choice to a GameState requires checking whether it’s one of the existing unique or duplicate choices:</p><pre>addToState :: Int -&gt; GameState -&gt; GameState<br>addToState n gs@(GameState dups uniqs)<br>  | Set.member n dups = gs<br>  | Set.member n uniqs = GameState (Set.insert n dups) (Set.delete n uniqs)<br>  | otherwise = GameState dups (Set.insert n uniqs)</pre><p>We can now directly compute the distribution of GameState corresponding to a set of <em>n</em> players playing moves with a given distribution. The use of simplify from the probability library here is crucial: it combines all the different paths that lead to the same outcome into a single case, avoiding the exponential explosion.</p><pre>stateDist :: Int -&gt; Distribution Double Int -&gt; Distribution Double GameState<br>stateDist n moves = go n (pure (GameState mempty mempty))<br>  where<br>    go 0 states = states<br>    go i states = go (i - 1) (simplify $ addToState &lt;$&gt; moves &lt;*&gt; states)</pre><p>Now it remains to determine whether a certain move can win, given the game state resulting from the remaining moves.</p><pre>win :: Int -&gt; GameState -&gt; Bool<br>win n (GameState dups uniqs) =<br>  not (Set.member n dups) &amp;&amp; maybe True (&gt; n) (Set.lookupMin uniqs)</pre><p>Finally, we update the function that computes win probabilities to use this new code.</p><pre>expectedOutcomesTo :: Int -&gt; Int -&gt; Distribution Double Int -&gt; [Double]<br>expectedOutcomesTo n m dist = [probability (win i) states | i &lt;- [1 .. m]]<br>  where<br>    states = stateDist (n - 1) dist</pre><p>The result is that while I previously had to leave the code running overnight to compute the <em>n </em>= 8 case, I can now easily compute cases up to 15 players with enough patience. This would involve computing the winner for about a quadrillion games in the naive code, making it hopeless , but the simplification reduces that to something feasible.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2c94Ku_cckZP97a8VWA71A.png" /><figcaption>Nash equilibria for 2 through 15 players</figcaption></figure><p>It seems that once you leave behind small numbers of players where odd combinatorial things happen, the equilibrium eventually follows a smooth pattern. I suppose with enough players, the probability for every number would peak and then decline, just as we see for 4 and 5 here, as it becomes worthwhile to spread your choices even further to avoid duplicates. That’s a nice confirmation of my intuition.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0dd944082e2c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Collatz and Base 2 / Base 3 Duality]]></title>
            <link>https://cdsmithus.medium.com/collatz-and-base-2-base-3-duality-01d44cdfbe5a?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/01d44cdfbe5a</guid>
            <category><![CDATA[collatz-conjecture]]></category>
            <category><![CDATA[mathematics]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Sun, 28 Jul 2024 23:01:16 GMT</pubDate>
            <atom:updated>2024-07-30T18:20:45.362Z</atom:updated>
            <content:encoded><![CDATA[<p>It might be obvious from my last two articles that I’ve been thinking about the Collatz conjecture a bit. Here, I can tie some of these ideas together in a surprising and really striking way.</p><p>Some of this I covered in earlier posts, but I’m going to construct things a little differently, so I’ll start from scratch. The Collatz conjecture is about the function <em>f</em>(<em>n</em>) defined to be <em>n</em>/2 if <em>n</em> is even, or 3<em>n</em>+1 if <em>n</em> is odd. Starting with some number (say, 7, for example) we can apply this function repeatedly to get 7, then 22, then 11, then 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, and then we’ll repeat 4, 2, 1, 4, 2, 1, and so on forever. The conjecture is that no matter which positive integer you start with, you’ll always end up in that same loop of 4, 2, and 1.</p><p>For reference, it’s going to be more convenient for us to work with something called the <em>shortcut</em> Collatz map. The idea here is that when <em>n</em> is odd, we already know that 3<em>n</em>+1 will be even. So we can shortcut one iteration by jumping straight to (3<em>n</em>+1)/2, just avoiding a separate pass for the division by two that we already know will be necessary.</p><p>We tend to work in base 10 as society, but the question I asked in an article a couple weeks ago is what happens if you perform this computation in base 2 or 3, instead.</p><ul><li>In base 2, it’s trivial to decide if a number is even or odd, and if it’s even, to divide by two. You just look at the least significant bit, and drop it if it’s a zero!</li><li>In base 3, it’s trivial to compute 3<em>n</em>+1. You just add a 1 digit to the end of the number!</li></ul><p>We could go either way, really, and in <a href="https://medium.com/@cdsmithus/collatz-computations-in-base-2-and-3-af04976d7826">my original article</a> I explored both computations to see what they looked like. This time, we’ll first head deep into the base 2 side, and see where it leads us.</p><h4>Collatz in Base 2</h4><p>When computing the Collatz function in base 2, the computationally significant part is to multiply a base 2 number by 3. We can work this out in the standard algorithm we all learned in elementary school, working from right to left, and keeping track of a carry at each step.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/280/1*GPBgwA1yerOgh8CBmqIOVQ.png" /></figure><p>We can even enumerate the rules:</p><ul><li>If the next bit is a 0 and the carry is a 0, then output a 0 and carry a 0.</li><li>If the next bit is a 1 and the carry is a 0, then output a 1 and carry a 1.</li><li>If the next bit is a 0 and the carry is a 1, then output a 1 and carry a 0.</li><li>If the next bit is a 1 and the carry is a 1, then output a 0 and carry a 2.</li><li>If the next bit is a 0 and the carry is a 2, then output a 0 and carry a 1.</li><li>If the next bit is a 1 and the carry is a 2, then output a 1 and carry a 2.</li></ul><p>and represent this using a finite state machine with a transition diagram.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/142/1*CujHM5u6q2_gP_SLoK8H9w.png" /></figure><p>This machine isn’t too hard to understand, really. When you see a 0, move up one state; when you see a 1, move down one state. When the carry is 1 (before moving), output the opposite bit; otherwise, output the same bit. That’s all there is to it.</p><p>We will make three small modifications to this simple state machine:</p><ul><li>In the Collatz map, we want to compute 3<em>n</em>+1, That just amounts to starting with a carry of 1, rather than 0.</li><li>Before computing 3<em>n</em>+1, we should divide by two until the number is odd. That amounts to adding a new “Start” state, or S for short, that ignores zeros on the right, and then acts like carry 1 when it encounters the first 1 bit. (Recall that we’re working from right to left!)</li><li>Finally, let’s compute the shortcut map as well: as discussed above, when we compute 3<em>n</em>+1, it will always be even. (We will always move the start state that acts like carry 1 into carry 2, and the arrow there emits a 0 in the least significant bit.) We do not emit the zero when moving from the start state to carry 2, so the bits that come out represent (3<em>n</em>+1)/2.</li></ul><p>The resulting maching looks like this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/227/1*thEmsYlaVBcMgyKyKVS1kA.png" /></figure><p>When fed the right-to-left bits of a non-zero number, this machine will compute what we might call a <em>compressed</em> Collatz map: dividing by 2 as long as the number remains even, and then compute (3<em>n</em>+1)/2 just like the shortcut Collatz map does.</p><h4>Iterating the Map</h4><p>The Collatz conjecture isn’t about a single application of this map, though, but rather about the trajectory of a number when the map is applied many times in succession. To simulate this, we’ll want a whole infinite array of these machines connected end to end, so the bits that leave each one arrive at the one after. Something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/306/1*0RpjR77JgvDqqLopsY_2CQ.png" /></figure><p>This is starting to get out of hand! So let’s simplify. Two things:</p><ul><li>Because their state transition diagrams are all the same, the only information we need about each machine is what state it’s in.</li><li>The S state never emits any bits, and you can never get back to S once you leave it, so we know that as soon as we see an S, the entire rest of the machines, the whole infinite tail, is still sitting in the S state waiting for bits. We need not worry about these states at all.</li><li>Once we’re done feeding in the non-zero digits of the input number, any machines in state 0 also become uninteresting. The rest of the inputs will all be zero, they will remain in state 0, and they will pass on that 0 bit of input unchanged. Again, we need not worry about these machines.</li></ul><p>With that in mind, we can trace what happens when we feed this array of cascading machines the bits of a number. Let’s try 7, since we saw its sequence already earlier on.</p><p>The output of each machine feeds into the next machine below it, and I’ve drawn this propagation of outputs to inputs of the next machine using green arrows. We’ll draw the digits of input from right to left, matching the conventional order of writing binary numbers, so in a sense, time flows from right to left here. Each state machine remembers its <em>state</em> as time progresses to the left, and I’ve drawn this memory of previous states using blue arrows. Notice that to play out the full dynamics, we need to feed in quite a few of the leading zeros on the input.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yhYeEB5rBY6BMy5kS3jCwg.png" /></figure><p>In the rows of green arrows, you can read off the outputs of each state machine in the sequence in binary:</p><ul><li>binary 111 = decimal 7</li><li>binary 1011 = decimal 11</li><li>binary 10001 = decimal 17</li><li>binary 11010 = decimal 26</li><li>binary 10100 = decimal 20</li><li>binary 1000 = decimal 8</li><li>binary 10 = decimal 2</li></ul><p>If we were to continue, the next rows would just repeat binary 10 (decimal 2) forever. This makes sense, because the way we defined the compressed Collatz map stabilizes at 2, rather than 1.</p><p>A second thing we can read from this map is the so-called <em>parity sequence</em> of the shortcut Collatz map. This is just the sequence of evens and odds that occur when the map is iterated on the starting number. When a column ends by emitting a 1 bit, bumping a new machine out of the S state, that indicates that the next value of the shortcut Collatz map will be odd. When it ends in a 0 bit, then the next value will be even.</p><p>There’s quite a lot of interesting theory about the parity sequences of the shortcut Collatz map! It turns out that every possible parity sequence is generated by a unique 2-adic integer, which I defined in <a href="https://medium.com/@cdsmithus/the-collatz-step-and-2-adic-integers-6f003efaf81c">my previous article</a>, so the 2-adic integers are in one-to-one correspondence with parity sequences. We can, in fact, compute the reverse direction of this one-to-one correspondence as well using state arrays like this one. Every eventually repeating parity sequence comes from a rational number, via the canonical embedding of the rationals into the 2-adic numbers. (The converse, though, that acyclic parity sequences only come from irrational 2-adic integers, is conjectured but not known!)</p><p>Because every parity sequence comes from a unique 2-adic integer, if we could show that every positive integer eventually leads to alternating even and odd numbers in its parity sequence, this would prove that the Collatz conjecture is true. Now we have a new way of looking at that question. Among the 2-adic integers, the positive integers are those that eventually have an infinite number of leading 0s. So we can ask instead, from any starting <em>state</em> sequence of this array of machines, when feeding zeros into the sequence forever, do we eventually (ignoring machines at the beginning that have reached the 0 state) reach only a single machine alternating through states 1, 2, 1, 2, etc.?</p><p>This isn’t an easy question, though. Certainly, feeding zeros into the array on the left will bump the state of the top-most machines down to zero. However, the bits continue to propagate through the machine, possibly pushing <em>new</em> machines out of their starting states and so appending them on the bottom! There is something of a race between these two processes of pruning machines on the top and adding them on the bottom, and we would need to show that the pruning wins that race.</p><h4>From Base 2 to Base 3</h4><p>As we investigate this race, we discover something surprising about the behavior of the state sequences when leading zeros are fed into the top machine. If you read the machine states in the blue arrows, starting at the third column from the right after all the non-zero bits of input have been fed in, we can interpret those state sequences as a ternary (base 3) number! And we get quite a familiar progression:</p><ul><li>ternary 222 = decimal 26</li><li>ternary 111 = decimal 13</li><li>ternary 202 = decimal 20</li><li>ternary 101 = decimal 10</li><li>ternary 12 = decimal 5</li><li>ternary 22 = decimal 8</li><li>ternary 11 = decimal 4</li><li>ternary 2 = decimal 2</li><li>ternary 1 = decimal 1</li></ul><p>That’s the shortcut Collatz sequence again! Rather than starting at 7, we jumped three numbers ahead because it took those three steps to feed in the non-zero bits of 7, so we missed 7, 11 and 17 and went straight to 26. Then we continue according to the same dynamics.</p><p>This coincidence where state sequences can be interpreted as ternary numbers is suprising, but is it useful? It can be a revealing way to think about Collatz sequences. Here’s an example.</p><p>Numbers of the form 2<em>ⁿ</em>-1 have a binary representation consisting of <em>n</em> consecutive 1s. If we trace what happens to the state sequence, we find that each 1 we feed to this state sequence propagates all the way to the end to append another 2 to the sequence, leaving us with a state sequence consisting of <em>n</em> consecutive 2s. As a ternary number, that is 3<em>ⁿ</em>-1. If the above is correct, then, we can conclude that iterating the shortcut Collatz map <em>n</em> times starting with 2<em>ⁿ</em>-1 should yield 3<em>ⁿ</em>-1 as a result.</p><p>In fact, this isn’t hard to prove. We can prove the more general statement that for all <em>i</em> ≤ <em>n</em>, iterating the shortcut Collatz map <em>i</em> times on 2<em>ⁿ</em>-1 gives a result of 3<em>ⁱ</em>2<em>ⁿ⁻ⁱ</em>-1. A simple induction suffices. If <em>i</em> = 0, then the result is immediate. Assuming it’s true for <em>i</em>, and that <em>i</em> + 1 ≤ <em>n</em>, we know that 3<em>ⁱ</em>2<em>ⁿ⁻ⁱ</em>-1 is odd, so applying the shortcut Collatz map one more time yields (3(3<em>ⁱ</em>2<em>ⁿ⁻ⁱ</em>-1)+1)/2, which simplifies to establish the property for <em>i</em> + 1 as well, completing the induction. Now let <em>i</em> = <em>n</em> to recover the original statement.</p><p>The proof was simple, but the idea came from observing the behavior of this state machine. And this is an interesting observation: 3<em>ⁿ</em>-1 grows asymptotically faster than 2<em>ⁿ</em>-1, so it implies that there is no bound on the factor by which a number might grow in the Collatz sequence. We can always find some arbitrarily large <em>n</em> that grows to at least about 1.5<em>ⁿ</em> times its original value.</p><h4>From Base 3 to Base 2</h4><p>Recall that in base 3, computing 3<em>n</em>+1 is easy, but it’s dividing by two that becomes a non-trivial computation. Back in elementary school again, we learned about long division, an algorithm for dividing numbers digit by digit, this time left to right. To do this, we divide each digit in turn, but keep track of a remainder that propagates from digit to digit. We can also draw this as a state machine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/143/1*1AQ8gXC3O-ed15Va3HT8vw.png" /></figure><p>To extend this to the shortcut Collatz map, we need to look for a remainder when the division completes. This means that we’ll need to feed our state machine not just the ternary digits 0, 1, and 2, but an extra “Stop” signal (S for short) indicating the number is complete. Since the result may be longer than the input, it will be convenient to send an infinite sequence of these S signals, giving the machine time to output all of the digits of the result before it begins to produce S signals, as well. Upon receiving this S signal, if there is a remainder, then the input was odd, so our state machine needs to add another 2 onto the end of the sequence of ternary digits to complete the computation of (3<em>n</em>+1)/2 before emitting its own S signals.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/143/1*LzIela1ZkqB5YXP_kv62_Q.png" /></figure><p>Just as before, we’re interested not in a single application of this state machine, but the behavior of numbers under many different iterations, so we will again chain together an infinite number of these machines, feeding the ternary digits (or S signals) that leave each one into the next one as inputs. This time I’ll draw the ternary digits flowing from right to left.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/752/1*UiAypOpMCEbX60kNNGSKyA.png" /></figure><p>Let’s try to simplify this picture.</p><ul><li>Again, all the state machines share the same transition diagram, so we need only note which state each machine is in.</li><li>Once a machine (or the input) starts emitting S signals, it will never stop, so we need not concern outselves with these machines.</li><li>Because the machines start in state 0 and machines in state 0 always decrease the ternary digit so no single digit can change more than two of these into non-zero states before it becomes zero itself, we’ll always encounter an infinite tail of 0 states to the left, which are similarly uninteresting.</li></ul><p>With those simplifications in mind, we can work through the behavior of these machines starting with the input 7, which is 21 in base 3. This time, input digits (time, essentially) flows from top to bottom, while the iterations of the state machine are oriented from right to left. The green arrows represent the memory of state over time, and the blue arrows represent input and output digits.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2VSjB3FFqw2BnBgoaHO7ZA.png" /></figure><p>Following the flow from right to left and reading down the blue arrows representing ternary digits, we can see the ternary values from the shortcut Collatz map computed by the state machines, read from top to bottom. We might ask a question similar to the earlier one: can we show that, starting from any state and throwing S signals at these state machines from the right, it somehow simplifies to the sequence 10 (a 0, followed by a 1 to its left), which indicates we’ve reached the cyclic orbit at 1, 2 in the shortcut Collatz sequence?</p><p>In looking at this, as you likely guessed, we find that the state sequences when read from left to right from green arrows (starting from the second row down, after all the input digits have been fed in) give the binary form of the <em>compressed</em> Collatz map. That’s the one that even further shortens the shortcut Collatz map by folding all the divisions by two so they happen implicitly before each odd value is processed. Starting with base 3, then, we end up back in base 2!</p><p>What’s going on? It’s easy to see that the diagram above is the same as the one from binary earlier, except for the addition of two rows at the top where we’re still feeding in the ternary digits, and some uninteresting state propagation from machines that are already emitting S signals, and swapping the interpretation of the axes. But why?</p><p>Let’s compare the state machines:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*QrOKCWBTwy96hkHELfvw6g.png" /></figure><p>They look quite different… but this is an illusion created by a biased presentation. These diagrams emphasize the state structure, but relegate the input structure to text labels on the arrows. We can instead draw both diagrams at once in this way:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/195/1*wqKOpyvbOvmMgVYInyeDPQ.png" /></figure><p>In the base 2 case, we can interpret the rows as representing bits of input, and the columns as states: three carries, and the Start state S. In the base 3 case, we can interpret the columns as inputs: three ternary digits, and the Stop signal S, and the rows as states. With either interpretation, though, the rule is the same: we are exchanging a presentation of a number from 0 through 5 as 3<em>b </em>+ <em>t</em> for a presentation as 2<em>t</em> + <em>b</em>, where <em>t</em> takes values 0, 1, or 2, while <em>b</em> takes values 0 or 1, and with the same rules for the special S token on the side of the least significant digits.</p><p>So in some deep sense, computing the Collatz trajectory in base 2 or base 3 is performing the <em>same</em> computation. This is true even though in base 2, we’re computing the compressed Collatz map, which has fewer iterations (but more digits to compute with), while in base 3, we’re computing the shortcut Collatz map, which has more iterations (but fewer digits to compute with). Somehow these differences are all dual to each other so the same thing happens in each.</p><p>Frankly, I find that very pretty.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=01d44cdfbe5a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Collatz Step and 2-adic Integers]]></title>
            <link>https://cdsmithus.medium.com/the-collatz-step-and-2-adic-integers-6f003efaf81c?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/6f003efaf81c</guid>
            <category><![CDATA[haskell]]></category>
            <category><![CDATA[mathematics]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Tue, 16 Jul 2024 15:51:07 GMT</pubDate>
            <atom:updated>2024-07-17T16:03:48.706Z</atom:updated>
            <content:encoded><![CDATA[<p>This is a follow-up to <a href="https://medium.com/@cdsmithus/collatz-computations-in-base-2-and-3-af04976d7826">my previous post</a> on Collatz in base 2 and 3. I got a response from a reader, Olaf K., who pointed out that the functions defined there work just fine not only on finite sequences of base 2/3 digits, but infinite sequences as well. In the base 2 case, where the digits were listed from right to left, this has a common mathematical interpretation. An integer with possibly non-zero bits extending infinitely to the left is a called 2-adic integer. And the function defined there yields some interesting observations when applied to the 2-adic integers!</p><h4>Brief introduction to 2-adic integers</h4><p>A standard binary integer is a finite sequence of bits, either 0 or 1, with each bit having a value equal to some power of two. Because any non-negative integer can be written as a sum of powers of two, it can be written in this way.</p><p>But a <em>finite</em> sequence isn’t exactly right. We can always make that sequence longer, incorporating greater powers of two, by adding zeros on the left side. For this reason, if we think of a binary number as a finite sequence, we get non-unique representations: one with a 1 as the largest digit, but others that add leading zeros on the left. This is messy, so in general we tend to think of a binary integer as having <em>infinitely</em> many bits, but with the constraint that only finitely many of them can be non-zero. We don’t usually write the leading zeros, but that’s just a matter of notation. They are still there.</p><p>This leads to the obvious question: what happens if you remove the restriction that all but finitely many digits must be zero? The answer is the 2-adic integers. It turns out that we can write a lot of rational numbers as 2-adic integers. For example:</p><ul><li>Even without a negative sign, we can write -1 as …111, the 2–adic integer all of whose bits are 1. Why? Try adding one, and you’ll notice that the result is all 0s, so clearly this is the opposite of 1.</li><li>What happens when you multiply 3 (binary 11) by the 2-adic integer …01010101011? If you work it out, you’ll get 1. So that 2-adic integer is the multiplicative inverse of 3, making it effectively 1/3.</li></ul><p>In fact, it turns out that the 2-adic integers include all rational numbers with odd denominators! Not only that, but all of them ultimately end up with digits in a repeating pattern to the left, similar to how rational numbers in traditional decimal representations end up with digits in a repeating pattern to the right. (There are even irrational 2–adic integers that don’t repeat their digits; but they don’t correspond to the traditional irrational numbers, but rather to some completely new concept that doesn’t happen in traditional numbers!)</p><h4>The Collatz map on 2-adics</h4><p>The Collatz map on 2-adic integers can be defined in precisely the same way as it is on integers: even numbers are halved, while odd numbers are mapped to 3<em>n</em>+1. But hold on… if we can represent numbers like 1/3, what does it mean to be even or odd?</p><p>For arbitrary rationals, this would be a tricky question to answer, but in the 2–adic integers, there’s an easy answer: just look at the 1s place. If it’s 0, the number is even; if it’s 1, the number is odd. This is equivalent to saying that a rational number is even iff its <em>numerator</em> is even. And this notion is well-defined because we’ve already constrained the denominator to be odd.</p><p>I’m now going to redefine the Collatz step function on binary numbers from my previous post, but with one difference: I’ll assume that the numbers are odd. Because every number therefore ends with a 1, we won’t represent the 1 explicitly, but rather let it be implied. This implied 1 is expressed by the OddBits newtype.</p><pre>data Bit = B0 | B1<br>newtype OddBits = XXX1 [Bit]<br>data State = C0 | C1 | C1_Trim | C2 | C2_Trim<br><br>threeNPlusOneDiv2s :: OddBits -&gt; OddBits<br>threeNPlusOneDiv2s (XXX1 bits) = XXX1 (go C2_Trim bits)<br>  where<br>    go C1_Trim [] = []<br>    go C1_Trim (B0 : bs) = go C0 bs<br>    go C1_Trim (B1 : bs) = go C2_Trim bs<br>    go C2_Trim [] = []<br>    go C2_Trim (B0 : bs) = go C1_Trim bs<br>    go C2_Trim (B1 : bs) = go C2 bs<br>    go C0 [] = []<br>    go C0 (B0 : bs) = B0 : go C0 bs<br>    go C0 (B1 : bs) = B1 : go C1 bs<br>    go C1 [] = [B1]<br>    go C1 (B0 : bs) = B1 : go C0 bs<br>    go C1 (B1 : bs) = B0 : go C2 bs<br>    go C2 [] = [B0, B1]<br>    go C2 (B0 : bs) = B0 : go C1 bs<br>    go C2 (B1 : bs) = B1 : go C2 bs</pre><p>This function, in a single pass, multiplies an odd number by 3, adds 1, then divides by 2 as many times as needed to make the result odd. Therefore, this is a map from the <em>odd</em> numbers to other <em>odd</em> numbers. The states represent:</p><ol><li>The amount carried from the previous bit when multiplying by 3.</li><li>Whether the lower-order bits are all zeros, in which case we should continue to trim zeros instead of emit them.</li></ol><p>This function still handles finite lists, but you can generally ignore those equations, since they are equivalent to extending with 0 bits to the left. And as Olaf suggests, the function extends to the 2-adic numbers by operating on infinite lists. (That is, except for one specific input: …010101, on which the function hangs non-productively. That’s because this 2-adic integer corresponds to the rational -1/3, and 3(-1/3) + 1 = 0, which can never be halved long enough to yield another odd number!)</p><h4>Fixed points</h4><p>The Collatz conjecture amounts to finding the orbits of the Collatz map, which fall into two categories: periodic orbits, which repeat infinitely, and divergent orbits, which grow larger indefinitely without repeating. Among positive integers, the conjecture is that the only orbit is the periodic one that ends in 4, 2, 1, 4, 2, 1, 4, 2, 1…</p><p>Since we’re skipping the even numbers, our step function has the property that <em>f</em>(1) = 1, making 1 a fixed point. Not all periodic orbits are fixed points, but it’s natural to ask whether there are any <em>other</em> fixed points of this map. Let’s explore this question!</p><p>We start by looking only at the non-terminating equations for the recursive definition. (Recall that the terminating equations are really just duplicates of these, since leading zeros are equivalent to termination.)</p><pre>    go C1_Trim (B0 : bs) = go C0 bs<br>    go C1_Trim (B1 : bs) = go C2_Trim bs<br>    go C2_Trim (B0 : bs) = go C1_Trim bs<br>    go C2_Trim (B1 : bs) = go C2 bs<br>    go C0 (B0 : bs) = B0 : go C0 bs<br>    go C0 (B1 : bs) = B1 : go C1 bs<br>    go C1 (B0 : bs) = B1 : go C0 bs<br>    go C1 (B1 : bs) = B0 : go C2 bs<br>    go C2 (B0 : bs) = B0 : go C1 bs<br>    go C2 (B1 : bs) = B1 : go C2 bs</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/368/1*f_61no5rDdCQgIZMLbJnOA.png" /><figcaption>State transition diagram for Collatz step</figcaption></figure><p>These observations will be relevant:</p><ol><li>We start in the state C2_Trim</li><li>The Trim states do not emit bits to the result, only consuming them. Therefore, the output will lag behind the input by some number of bits depending on how long evaluation lingers in these Trim states.</li><li>Once we leave the Trim states, we can never re-enter them. Inputs and outputs will then match exactly, so the lag stays the same forever.</li><li>If we’re searching for inputs that evaluate in a certain way, the bits of the input are completely determined by whether we want to stay in Trim states or leave them, and then whether we want the next output to be a 0 or 1.</li></ol><p>Because of this, when searching for a fixed point of this function, the input value is entirely determined by one choice: for how many input bits do we choose to remain in the Trim states. Once that single choice is made, the rest of the input is entirely determined by that plus the desire for the input to be a fixed point.</p><p>Let’s work some out.</p><p><strong>Lag = 1</strong>. Here, we want to stay in the Trim states for only one bit of input. Then that bit must be a 1, since that’s what gets us out of the Trim state. From that point, we will stay in state C2, and in order to produce the 1 output bits to match the inputs, we’ll need to keep seeing 1s in the input! Then the fixed point here is XXX1 (repeat 1), which corresponds to the 2-adic integer …111.</p><p>We noted earlier that this 2–adic integer corresponds to -1. We can double-check that -1 is indeed a fixed point of the function that computes 3<em>n</em>+1 and then divides by 2 until the result is odd. To compute <em>f</em>(-1), we first compute 3(-1)+1 = -2, then divide by 2 to get -1, which is odd. So it is indeed a fixed point.</p><p><strong>Lag = 2</strong>. Here, we want to stay in the Trim states for two bits of input. That means we expect the first bit to be 0 so that we’ll switch to state C1_Trim, and then the second bit to be 0 again to transition us to the C0 state. At this point, we’re producing output, which must match the input bits already chosen, and the input bit we need will always be a 0 so as to produce the 0 that matches the input. Then the fixed point is XXX1 (repeat 0), and keeping in mind that there’s an implied 1 on the end, this corresponds to the 2-adic integer …0001, which is just 1.</p><p>This is the standard period orbit mentioned up above: 4, 2, 1, 4, 2, 1, which is just 1s when we skip the even numbers.</p><p>Now things start to get interesting:</p><p><strong>Lag = 3</strong>. To stay in the Trim states for exactly three bits of input, we need those bits to be 0, 1, and 1. This ends up in state C2, with the input sequence 011 left to match. The next input must therefore be 0, yielding a 0 as output, and leaving us in state C1 with 110 left to match. The next input must be 0 again, leaving us in C0 with 100 left to match. We need a 1 next, leaving us in C1 with 001 left to match. Then we need to see a 1 again to leave us in C2 with 011 left to match. That’s the same state and pending bits as we were in when we left the Trim states, so we’ve <em>finally</em> found a loop.</p><p>The fixed point that produces this behavior is XXX1 (cycle [0, 1, 1, 0]), and including the implied 1 on the end, this corresponds to the 2-adic integer …(0110)(0110)(0110)1. This turns out to correspond to the rational number 1/5. We can check that 3(1/5) + 1 = 8/5, and halving that three times yields 1/5 again, so this is indeed a fixed point of the map, even though it’s not an integer.</p><h4>Observations about fixed points</h4><p>A few observations can be made about the fixed points of this map:</p><ol><li>There are an infinite number of them. Every possible choice of lag, starting with one but increasing without bound, yields a fixed point, and they all must be different since they produce different behaviors.</li><li>There are only a countably infinite number of them. This is the <em>only</em> way to produce a fixed point, so the list of fixed points we compute in this way is complete. There are no others.</li><li>The 2-adic fixed points are all rational. Once we leave to Trim states, there’s only a finite amount of state involved in determining what happens from here: the state of the function implementation, together with the pending input bits remaining to match, which keeps the same length. We progress through this finite number of states indefinitely, so we must eventually reach the same state twice, and from that point, the bits will follow a repeating pattern. Therefore, interpreted as a 2–adic number, they will correspond to a <em>rational</em> value.</li><li>The only integer fixed points are 1 and -1. You can see this for non-negative integers by looking at the terminating equations in the original code: the longest terminating case produces two bits at the end before ending in trailing zeros, so the lag can be no greater than 2. Similar logic applies to negative integers, which have 1s extending infinitely to the left.</li></ol><p>In fact, if we work out what’s going on here, we find that fixpoints of this function are precisely 1 / (2<em>ⁿ</em> - 3) for <em>n</em> &gt; 0. (In fact, <em>n</em> = 0 yields -1/2, which is also a fixed point as a rational, but is not a 2-adic integer so it didn’t occur in our list.)</p><h4>Periodic points</h4><p>We can press further on this, and consider periodic points with period greater than one, by composing the function with itself and writing down the state machine that results. This grows more complex, as every additional iteration of the function adds a new choice about lag, yielding a larger-dimensional array of periodic points. The general form of the computation seems to remain the same, but the state diagrams grow increasingly daunting.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/711/1*o61quhbNVFNoR7mbmWFWKg.png" /><figcaption>State transition diagram for two Collatz steps</figcaption></figure><p>The diagram above gives the state transitions for the composition of two Collatz steps. The left two states are those where the first of the two steps has yet to produce a bit. The next six are states where the first is producing bits but the second is not. The final nine, then, represent the situation where both of the two composed steps is productive.</p><p>I have not labeled the outputs, but the general rule is that the trim states have no output, the non-trim states with an even number of occurrences of C1 will produce outputs that are the same as their inputs, and the states with odd occurrences of C1 produce outputs the opposite of their inputs.</p><p>Because there are now two kinds of trimming that happen, we can choose any combination of the two lags, giving a 2D array of points that repeat with period 2. The computations are similar to the above, so I’ll just give the results for lag 1, 2, and 3 in each dimension.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2Ff0ae97e587&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2Ff0ae97e587&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="247" frameborder="0" scrolling="no"><a href="https://medium.com/media/63f3f74afed7c389e34a492f2bf83e18/href">https://medium.com/media/63f3f74afed7c389e34a492f2bf83e18/href</a></iframe><p>The diagonal elements are not new. This is expected, though, because a periodic point of period 1 is also periodic with period 2. The off-diagonal elements yield three new period-2 orbits:</p><ul><li>-5, -7, -5, …</li><li>5/7, 11/7, 5/7, …</li><li>7/23, 11/23, 7/23, …</li></ul><p>Just as with fixed points, we can work out a closed form for the period two points. This time, we get (2^<em>m</em> + 3) / (2^(<em>m</em>+<em>n</em>) - 9). As we noticed above, this simplifies to the earlier formula for fixed points when <em>m</em> = <em>n</em>. (Hint: the denominator factors as a difference of squares.)</p><p>You might wonder if there’s a pattern here that continues to all periodic points, and indeed there is! On the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_rationals_with_odd_denominators">Wikipedia page about the Collatz Conjecture</a>, a formula is given for the unique rational number that generates any periodic sequence of even and odd numbers from the “shortcut” Collatz map. (The shortcut map is defined there as the map that divides by 2 once after each 3<em>n</em>+1 step.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/182/0*iUz413UvsdbFN7mv" /></figure><p>To translate this into our terms:</p><ul><li><em>m</em> is the period, which is also the number of lag values.</li><li><em>k</em>₀ = 0, because the function defined here can only be evaluated on odd numbers.</li><li>Each additional <em>kᵢ</em> is the sum of the first <em>i</em> lag values.</li><li><em>n</em> is the sum of all the lag values.</li></ul><p>We can make the interesting observation that the sign of the number is determined by the denominator: if <em>n</em> &gt; <em>m</em> log₂(3), or about 1.585 <em>m</em>, then the result will be positive. But <em>n</em>/<em>m</em> is also understood as the average of the lag values. So looking for positive periodic points amounts to choosing lag values with an average of greater than about 1.585. But perhaps not too much greater, if we want them to be integers, because we should not allow the denominator to grow too large. (Indeed, we saw in the fixed point case above that there was a bound on how large the lag could grow because the output needs to catch up!) Working out more precise upper bounds on lag would be an interesting step toward a search for periodic points.</p><p>The fact that this rational number is unique, left somewhat mysterious in the Wikipedia statement, comes down to the way that fixed points of these composed state machines are always determined by how long we linger in each of the trim states. The result on Wikipedia already implies that these are the only periodic points among the rationals with odd denominators. The analysis here also makes it clear that these are the only periodic points in the 2-adic integers, as well, so there are no irrational 2-adic periodic points of the Collatz map.</p><p>Of course, the trick would be to show that none of these rational values except for 1 are positive integers, and then that there are also no orbits that increase aperiodically. Actually solving the Collatz Conjecture is left as an exercise for the reader. :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6f003efaf81c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Collatz Computations in Base 2 and 3]]></title>
            <link>https://cdsmithus.medium.com/collatz-computations-in-base-2-and-3-af04976d7826?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/af04976d7826</guid>
            <category><![CDATA[collatz-conjecture]]></category>
            <category><![CDATA[mathematics]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Tue, 09 Jul 2024 04:49:06 GMT</pubDate>
            <atom:updated>2024-07-09T04:49:06.447Z</atom:updated>
            <content:encoded><![CDATA[<p>Every so often, the Collatz conjecture comes up in discussion forums I read, and I start to think about it again. I did for a bit this past weekend. Here are my thoughts this time around.</p><h3>The Problem</h3><p>A Collatz sequence starts with some positive integer <em>x</em>₀ and develops the sequence inductively as <em>xₙ</em>₊₁= <em>xₙ </em>/ 2 if <em>xₙ</em> is even, or 3<em>xₙ </em>+ 1 if <em>xₙ</em> is odd. For instance, starting with 13, we get:</p><ul><li><em>x</em>₀ = 13</li><li><em>x</em>₁ = 3(13) + 1 = 40</li><li><em>x</em>₂ = 40 / 2 = 20</li><li><em>x</em>₃ = 20 / 2 = 10</li><li><em>x</em>₄ = 10 / 2 = 5</li><li><em>x</em>₅ = 3(5) + 1 = 16</li><li><em>x</em>₆ = 16 / 2 = 8</li><li><em>x</em>₇ = 8 / 2 = 4</li><li><em>x</em>₈ = 4 / 2 = 2</li><li><em>x</em>₉ = 2 / 2 = 1</li><li><em>x</em>₁₀ = 3(1) + 1 = 4</li><li><em>x</em>₁₁ = 4 / 2 = 2</li><li><em>x</em>₁₂ = 2 / 2 = 1</li></ul><p>From there, it’s apparent that the sequence repeats 1, 4, 2, 1, 4, 2, … forever. That is one way for a Collatz sequence to end up. The famous question here, known as the Collatz Conjecture, is whether it’s the <em>only</em> way any such sequence can terminate. Not necessarily! There could be other cycles besides 1, 4, 2. Or there could be a sequence that keeps increasing forever without repeating a number. Or maybe that never happens. No one knows!</p><p>We do know a few things. First, if these things happen, they only happen with astronomically large numbers that even powerful computers haven’t been able to check by hand. We know that if even a single number is repeated, then that part of the sequence will repeat forever, since the whole tail of the sequence is determined by any single number in it. And we know that such a sequence cannot <em>decrease</em> forever, since Collatz sequences remain positive integers, so eventually would reach number that we know end up in the 1,4,2 loop. We also know that there <em>are</em> other loops in Collatz sequences that begin with negative integers, so the fact that there have been none found so far in the positive integers is at least a little surprising.</p><p>The Collatz Conjecture is famous because it’s probably one of the easiest unsolved math problem to understand the meaning of, for mathematical novices. There’s no Riemann zeta function to define. Just even and odd numbers, division by two, multiplication by three, and adding one. That doesn’t mean it’s easy to solve, though! Many mathematicians and countless novices have spent decades working on the problem, and there’s no promising road to a solution. The mathematician Erdős suggested that it’s not simply that no one has found the solution, but that mathematics is lacking even the basic tools needed to work on this problem.</p><h3>Collatz and Alternate Bases</h3><p>There are many, many ways to think about the Collatz conjecture, but one of them is to look at the computation in different bases. We’re not really attempting to find a more efficient way to <em>compute</em> Collatz sequences. If we cared about that, it would be far more efficient to use whatever representation our computing hardware is designed for! Rather, what we’re looking for here is the possibility of some kind of pattern in the computation that reveals something analytical about the problem.</p><p>Addition works essentially the same way the same regardless of base, but computations involved in multiplication and division are very dependent on the choice of base! Since the definition of the Collatz sequence two natural choices for computing Collatz sequences are base 2 (binary) and base 3 (ternary).</p><ul><li>In base 2, it’s trivial to decide whether a number is even or odd, and to divide by two. On the other hand, computing 3<em>n</em>+1 is less trivial, requiring a pass over potentially every digit in the number.</li><li>In base 3, the opposite happens. Computing 3<em>n</em>+1 is now trivial. But recognizing that a number is even and dividing by two now require a pass over every digit.</li></ul><p>Let’s jump into the details and see what happens.</p><h4>Base 3 in Detail</h4><p>Base 3 representations are appealing for the Collatz sequence because it’s trivial to compute 3<em>n</em>+1. It amounts to simply adding a 1 to the end of the representation, shifting everything else left (i.e., multiplying it by 3) to make room. If you have <em>n</em> = 1201 (decimal 46), for example, then 3<em>n</em>+1 = 12011 (demical 139).</p><p>The more difficult tasks are:</p><ul><li><strong>Determining whether the number is even or odd.</strong> Unlike decimal, we cannot simply look at the last digit. Instead, a number in base 3 is even if and only if it has an even number of 1s in its representation. That’s not hard to count, but it does require looking at the entire sequence of digits.</li><li><strong>Dividing by two.</strong> Given a sequence of base 3 digits, we can express the division algorithm on right-to-left numbers as a state machine using the long division algorithm with remainders as states (starting with zero), using the following division table.</li></ul><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2Fc055405da2&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2Fc055405da2&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="386" frameborder="0" scrolling="no"><a href="https://medium.com/media/3d64cb9693704a9c676622d805ff9308/href">https://medium.com/media/3d64cb9693704a9c676622d805ff9308/href</a></iframe><p>Let’s see how this table works with an example. Starting again with 1201 (decimal 46):</p><ul><li>We always start with a remainder of 0. The first digit is 1. That’s the second line of the table. The output digit is, therefore, 0, and the next remainder is 1.</li><li>A remainder of 1 and a digit of 2 is the last line of the table. It tells us to add a 2 to the output, and proceed with a remainder of 1.</li><li>A remainder of 1 and a digit of 0 is the fourth line. We add a 1 to the output, and proceed with a remainder of 1.</li><li>A remainder of 1 and a digit of 1 is the fifth line. We add a 2 to the output and proceed with a remainder of 0.</li><li>We’re now out of digits. The quotient is 0212 (decimal 23, but note that leading zero which we’ll talk about later!) and the remainder is 0.</li></ul><p>Naively, we would have to make two passes over the current number: one to determine whether it’s even or odd, and then again, if it’s even, to divide by two. We can avoid this, though, by remembering that if a number is odd, we intend to compute 3<em>n</em>+1, which will always be even (because the product of two odd numbers is odd, so adding one makes it even), so we’ll then divide that by two. A little algebra reveals that (3<em>n</em>+1)/2 = 3(n/2 - 1/2) + 2 = 3⌊<em>n</em>/2⌋ + 2 if <em>n</em> is odd.</p><p>What this means is that we can go ahead and halve <em>n</em> regardless of whether it’s even or odd. At the end, we’ll know whether there’s a remainder or not, and if so, we will already be in position to append a 2 (rather than a 1 as discussed earlier) to the halved number and rejoin the original sequence. This skips one step of the Collatz sequence, but that’s okay. If our goal is only to determine whether the sequence eventually reaches 1, it doesn’t change the answer if we take this shortcut.</p><p>Appending that 2 to the end of the number changes the meaning of our state transition table a little bit. Instead of automatically quitting when we reach the end of the current number, we’ll need a chance to append another digit at the end. We’ll add rows to the table for what to do <em>after</em> all the digits have been seen, and be explicit about when to terminate (i.e., finish processing).</p><p>There’s one more detail we can handle as we go: as we saw earlier, dividing by two can produce a leading zero at the beginning of the result, which is unnecessary. We can arrange to never produce that leading zero at all, so we don’t need to ignore or remove it later. We just need to remember where we’re just starting and therefore don’t need to write leading zeros. In that case, the remainder is always zero, so there’s only one state to add.</p><p>We get the following state transition table.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2F33863664b7&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2F33863664b7&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="570" frameborder="0" scrolling="no"><a href="https://medium.com/media/a72dece41d05e263ba3a5108e5bdac34/href">https://medium.com/media/a72dece41d05e263ba3a5108e5bdac34/href</a></iframe><p>Since there are no leading zeros in the representations, we need not concern ourselves with the case where the first digit encountered is a zero, but if you want to handle it, we can produce no output and remain in the <em>Just Starting</em> state, since it ought to change nothing. I’ve done so in the code below.</p><p>We can iterate this state machine on ternary numbers, and get consecutive values from the Collatz sequence, though slightly abbreviated because we combined the 3<em>n</em>+1 step with the following division by 2. The Collatz conjecture is now equivalent to the proposition that this iterated state machine will eventually produce only a single 1 digit.</p><p>I’ve implemented this in the Haskell programming language as follows:</p><pre>import Data.Foldable (traverse_)<br>import System.Environment (getArgs)<br><br>data Ternary = T0 | T1 | T2 deriving (Eq, Read, Show)<br><br>step3 :: [Ternary] -&gt; [Ternary]<br>step3 = si <br>  where<br>    si (T0 : xs) = si xs<br>    si (T1 : xs) = s1 xs<br>    si (T2 : xs) = T1 : s0 xs<br>    si [] = []<br><br>    s0 (T0 : xs) = T0 : s0 xs<br>    s0 (T1 : xs) = T0 : s1 xs<br>    s0 (T2 : xs) = T1 : s0 xs<br>    s0 [] = []<br><br>    s1 (T0 : xs) = T1 : s1 xs<br>    s1 (T1 : xs) = T2 : s0 xs<br>    s1 (T2 : xs) = T2 : s1 xs<br>    s1 [] = [T2]<br><br>main :: IO ()<br>main = do<br>  [n] &lt;- fmap read &lt;$&gt; getArgs<br>  traverse_ print (iterate step3 n)</pre><p>And here’s a sample result:</p><pre>$ cabal run exe:collatz3 ‘[T1, T2, T0, T1]’ | head -15<br>[T1,T2,T0,T1]<br>[T2,T1,T2]<br>[T1,T0,T2,T2]<br>[T1,T2,T2,T2]<br>[T2,T2,T2,T2]<br>[T1,T1,T1,T1]<br>[T2,T0,T2]<br>[T1,T0,T1]<br>[T1,T2]<br>[T2,T2]<br>[T1,T1]<br>[T2]<br>[T1]<br>[T2]<br>[T1]</pre><p>Starting with 1201 (decimal 46), we get 212 (decimal 23), 1022 (decimal 35), 1222 (decimal 53), 2222 (decimal 80), 1111 (decimal 40), 202 (decimal 20), 101 (decimal 10), 12 (decimal 5), 22 (decimal 8), 11 (decimal 4), 2, 1, 2, 1, … As predicted, that’s the Collatz sequence, except for the omission of 3<em>n</em>+1 terms since their computation is merged into the following division by two.</p><h4>Base 2 in Detail</h4><p>So what happens in base 2 (binary)? It’s a curiously related but different story!</p><ul><li>Determining whether a number is even or odd is trivial: just look at the last bit and observe whether it is 0 or 1.</li><li>Dividing an even number by two is trivial: once you observe that the last digit is a 0, simple delete it, shifting the remaining bits to the right to fill in.</li><li>However, computing 3<em>n</em>+1 becomes less trivial, now requiring a pass over the entire digit sequence.</li></ul><p>Since the hard step is multiplication, and the algorithmically natural direction to perform multiplication is from right to left, we can reverse the order in which we visit the bits, progressing from the least-significant to the most-significant. This is a change from the base 3 case, where division (the inverse of multiplication) was easier to perform in the left-to-right order.</p><p>We can start as before, by writing down a simple state transition table for a state machine that multiplies a binary number by 3. The state here is represented by the number carried to the next column.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2F62cf789048&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2F62cf789048&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="386" frameborder="0" scrolling="no"><a href="https://medium.com/media/931082c4a37131c67c30ffe618bfa188/href">https://medium.com/media/931082c4a37131c67c30ffe618bfa188/href</a></iframe><p>(You might recognize this as the same table we already wrote down for halving a ternary number! The only differences are the column headers: the role of states and digits are swapped, and that we must traverse the digits in the opposite order.)</p><p>There’s one unfortunate subtlety to this table, and it has to do with leading zeros again. In principle, we think of a number in any base as having an infinite number of leading zeros on the left. In order to get correct results from this table, we need to continue consuming more digits until <em>both</em> the remaining digits and the current remainder are all zero. To express this, we’ll again need to convert our transition table to use explicit termination. This is so that we can stop at exactly the right point and not emit any unnecessary trailing zeros.</p><p>But what about the rest of the logic of the Collatz sequence?</p><ul><li>We should add one after tripling to compute 3<em>n</em>+1. That would also require a pass over potentially the entire number in the worst case… but we’re in luck. We can combine the two tasks just by starting from the <em>Carry 1</em> state when following this state transition diagram.</li><li>If the number is even, we should divide by two. Recall how in the ternary case, we merged some of the halving with the 3n+1 computation? This time, we can merge all the halving! Dividing even numbers by two just means dropping trailing zeros from the right side of the representation. Since we’re working right to left, it’s easy to add one more state that ignores trailing zeros at the start of the input.</li></ul><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2F05091c6e26&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2F05091c6e26&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="524" frameborder="0" scrolling="no"><a href="https://medium.com/media/1e97abe0d424392b8034b1f1824b6f7a/href">https://medium.com/media/1e97abe0d424392b8034b1f1824b6f7a/href</a></iframe><p>We need to be a little careful here, because this version of the Collatz sequence never emits a 1, so looking for a 1 in the sequence is doomed! Instead, the numbers displayed are only the ones immediately after a 3<em>n</em>+1 step, so the final behavior (for all numbers computed so far, anyway) is an infinitely repeating sequence of 4s. We know from earlier that 4 is part of the 1,4,2 cycle, so seeing 4s is enough to know that the full Collatz sequence passes through 1.</p><p>We can fix this by remembering refusing to emit any of the trailing zeros. Now we’re ignoring trailing zeros, but also never producing them. The blowup in the number of states needed to keep track of whether a zero has been emitted yet is unfortunate, because we may pass through multiple states before emitting the first non-zero digit. Each of those states needs a copy that handles this new case. Here’s our final transition table.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsheetsu.com%2Ftables%2F0d28822f15&amp;dntp=1&amp;display_name=Sheetsu&amp;url=https%3A%2F%2Fsheetsu.com%2Ftables%2F0d28822f15&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=sheetsu" width="700" height="708" frameborder="0" scrolling="no"><a href="https://medium.com/media/25712c1dd798a9e486aeeb209fb9eed7/href">https://medium.com/media/25712c1dd798a9e486aeeb209fb9eed7/href</a></iframe><p>Here’s the implementation in Haskell:</p><pre>import Data.Foldable (traverse_)<br>import System.Environment (getArgs)<br><br>data Binary = B0 | B1 deriving (Eq, Show, Read)<br><br>step2 :: [Binary] -&gt; [Binary]<br>step2 = si<br>  where<br>    si (B0 : xs) = si xs<br>    si (B1 : xs) = s2i xs<br>    si [] = [B1]<br><br>    s0 (B0 : xs) = B0 : s0 xs<br>    s0 (B1 : xs) = B1 : s1 xs<br>    s0 [] = []<br><br>    s1 (B0 : xs) = B1 : s0 xs<br>    s1 (B1 : xs) = B0 : s2 xs<br>    s1 [] = [B1]<br><br>    s1i (B0 : xs) = B1 : s0 xs<br>    s1i (B1 : xs) = s2i xs<br>    s1i [] = [B1]<br><br>    s2 (B0 : xs) = B0 : s1 xs<br>    s2 (B1 : xs) = B1 : s2 xs<br>    s2 [] = [B0, B1]<br><br>    s2i (B0 : xs) = s1i xs<br>    s2i (B1 : xs) = B1 : s2 xs<br>    s2i [] = [B1]<br><br>main :: IO ()<br>main = do<br>  [n] &lt;- fmap read &lt;$&gt; getArgs<br>  traverse_ print (iterate step2 n)</pre><p>And a result:</p><pre>$ cabal run exe:collatz2 &#39;[B1, B0, B1, B1, B0, B1]&#39; | head -80<br>[B1,B0,B1,B1,B0,B1]<br>[B1,B0,B0,B0,B1]<br>[B1,B0,B1,B1]<br>[B1,B0,B1]<br>[B1]<br>[B1]<br>[B1]</pre><p>We start with 101101 (45 in decimal). We triple and add one to get 136, then half to get 68, then 34, then 17, which is the next value that appears (10001 = 17 in decimal). We triple and add one to get 52, then half to get 26, then 13, which is 1101 in binary, and the third number in the list. (Remember the bits are listed from right to left!) Now triple and add one to get 40, and half until you reach 5, which is 101 in binary and the fourth number in the list. Finally, triple and add one to get 16, and half until you reach 1, which is where it stays.</p><h3>Analysis</h3><p>Is this a promising avenue to attack the Collatz Conjecture? Almost surely not. I’m not sure anyone knows a promising way to solve the problem. Nevertheless, we can ask what it might look like if one were to use this approach to attempt some progress on the conjecture.</p><p>One way (in fact, in some sense, the only way) to solve the Collatz Conjecture is to find some kind of quantity that:</p><ol><li>Takes its minimum possible value for the number 1.</li><li>Always decreases from one element of a Collatz sequence to the next, except at 1.</li><li>Cannot decrease forever.</li></ol><p>If such a quantity exists, then a Collatz sequence must eventually reach 1, so the Collatz Conjecture must be true — and conversely, in fact, if the Collatz Conjecture is true, then such a quantity must exist, since the number of steps to reach 1 would then be exactly such a quantity. This is equivalent to the original conjecture, which is why I commented that proving this is the only way to solve it! But this way of looking at the conjecture is interesting because it lets you define any quantity you like, as long as it has those three properties.</p><p>We know a lot of things that this quantity <em>isn’t</em>. It can’t be just the magnitude of the number, since that can increase with the 3<em>n</em>+1 rule. It also can’t be the number of digits (in any base), since that can increase sometimes, as well. Plenty of people have looked for other quantities that work. It’s useful to me to think of the quantity as a measure of the “entropy” (or rather its opposite, since it’s decreasing). It’s something you lose any time you take a step, and this tells you that eventually you will reach some minimum state, which must be the number 1.</p><p>Just guessing a quantity is unlikely to work. But if you can come to some understanding of the behavior of these computations, it’s conceivable there’s a quantity embedded in them somewhere that satisfies these conditions. If this entropy value is calculated digit by digit, you may be able to isolate how it changes in response to each of these state transition rules.</p><p>It is, at the very least, one point of view from which one might start thinking. I never claimed to have any answers! This was always just a random train of thought.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af04976d7826" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Semigroup of Exponentially Weighted Moving Averages]]></title>
            <link>https://cdsmithus.medium.com/the-semigroup-of-exponentially-weighted-moving-averages-860a3e28409d?source=rss-18bd5acaea78------2</link>
            <guid isPermaLink="false">https://medium.com/p/860a3e28409d</guid>
            <category><![CDATA[algebra]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[haskell]]></category>
            <category><![CDATA[mathematics]]></category>
            <dc:creator><![CDATA[Chris Smith]]></dc:creator>
            <pubDate>Sun, 30 Jun 2024 23:21:28 GMT</pubDate>
            <atom:updated>2024-06-30T23:21:28.791Z</atom:updated>
            <content:encoded><![CDATA[<p>This is just a quick note about some more interesting algebraic structure, and how that structure can help with generalizing an idea. None of this is new, but it’s the perspective it sheds on the problem solving process that was interesting to me.</p><h4>Defining the EWMA</h4><p>And exponentially weighted moving average or (EWMA) is a technique often used for keeping a running estimate of some quantity as you make more observations. The idea is that each time you make a new observation, you take a weighted average of the new observation with the old average.</p><p>For a quick example, suppose you’re checking the price of gasoline every day at a nearby store.</p><ol><li>On the first day it’s <strong>$3.10</strong>.</li><li>The second day it’s $3.20. You take the average of $3.10 and $3.20, and get <strong>$3.15</strong> for your estimate.</li><li>The next day, it’s $3.30. You take the average of $3.15 and $3.30, and get <strong>$3.225</strong> for your estimate.</li></ol><p>This is not just the average of the three data points, which would be $3.20. Instead, the EWMA is biased in favor of more recent data. In fact, if you took these observations for a month, the first day of the month would be only about a billionth of the final result — basically ignored entirely — while the last day would amount for fully half. That’s the point: you’re looking for an estimate of the <em>current</em> value, so older historical data is less and less important. But you still want to smooth out the sudden jumps up and down.</p><p>In the example above, I was taking a straight average between the previous EWMA and the new data point. That leads for very short-lived data, though. More generally, there’s a parameter, normally called α, that tells you how to weight the average, giving this formula:</p><p>EWMAₙ₊₁ = <em>x</em>ₙ₊₁ α + EWMAₙ (1-α)</p><p>In the extreme, if α = 1, you ignore the historical data and just look at the most recent value. For smaller values of α, the EWMA takes longer to adjust to the current value, but smooths out the noise in the data.</p><h4>The EWMA Semigroup</h4><p>Traditionally, you compute an EWMA one observation at a time, because you only make one observation at a time and keep a running average as you go. But when analyzing such a process, you may want to compute the EWMA of a large set of data in a parallel or distributed way. To do that, you’re going to look for a way to map the observations from the list into a semigroup. (<a href="https://medium.com/@cdsmithus/monoids-are-composable-list-summarizers-77d2baf23ffc">See my earlier article</a> on the relationship between monoids and list processing.)</p><p>We’ll start by looking at what the EWMA looks like on a sequence of data points <em>x</em>₁, <em>x</em>₂, …, <em>x</em>ₙ.</p><ul><li>EWMA₁ = <em>x</em>₁</li><li>EWMA₂ = <em>x</em>₁ (1-α) + <em>x</em>₂ α</li><li>EWMA₃ = <em>x</em>₁ (1-α)² + <em>x</em>₂ α (1-α) + <em>x</em>₃ α</li><li>EWMA₄ = <em>x</em>₁ (1-α)³ + <em>x</em>₂ α (1-α)² + <em>x</em>₃ α (1-α) + <em>x</em>₄ α</li></ul><p>The first term of this sequence is funky and doesn’t follow the same pattern as the rest. But that’s because it was always different: since you don’t have a <em>prior average</em> at the first data point, you can only take the first data point itself as an expectation.</p><p>That’s a problem if we want to generate an entire semigroup from values that represent a single data point, since they would seem to <em>all</em> fall into this special case. We can instead treat the initial value as acting in <em>both</em> ways: as a prior expectation, <em>and</em> as an update to that expectation, giving something like this:</p><ul><li>EWMA₁ = <em>x</em>₁<strong> </strong>(1-α) + <em>x</em>₁ α</li><li>EWMA₂ = <em>x</em>₁<strong> </strong>(1-α)² + <em>x</em>₁ α (1-α) + <em>x</em>₂ α</li><li>EWMA₃ = <em>x</em>₁ (1-α)³ + <em>x</em>₁ α (1-α)² + <em>x</em>₂ α (1-α) + <em>x</em>₃ α</li><li>EWMA₄ = <em>x</em>₁ (1-α)⁴ + <em>x</em>₁ α (1-α)³ + <em>x</em>₂ α (1-α)² + <em>x</em>₃ α (1-α) + <em>x</em>₄ α</li></ul><p>The first term is the contribution of the prior expectation, but even the single observation case now has update terms, as well.</p><p>There are now two parts to the semigroup: a prior expectation, and an update rule. There’s an obvious and trivial semigroup structure to prior expectations: just take the first value and discard the later ones.</p><p>The semigroup structure on the update rules is a little more subtle. The key is to realize that the rule for updating an exponentially weighted moving average always looks a bit like the formula for EWMA₁ above: a weighted average between the prior expectation and the new target value. While single observations should use the same weight (α), composing multiple observations amounts to adjusting to a combined target value with a larger weight</p><p>It takes a little algebra to derive the new weight and target value for a composition, but it amounts to this. If:</p><ul><li><em>f</em>₁(<em>x</em>) = <em>x</em> (1-α₁) + <em>x</em>₁ α₁</li><li><em>f</em>₂(<em>x</em>) = <em>x</em> (1-α₂) + <em>x</em>₂ α₂</li></ul><p>Then:</p><ul><li><em>(f</em>₁ ∘ <em>f</em>₂)(<em>x</em>) = <em>x</em> (1-α₃) + <em>x</em>₃ α₃</li><li>α₃ = α₁ + α₂ - α₁ α₂</li><li><em>x</em>₃ = (<em>x</em>₁ α₁ + <em>x</em>₂ α₂ - <em>x</em>₂ α₁ α₂) / α₃</li></ul><p>This can be defunctionalized to only store the weight and target values. In Haskell, it looks like this:</p><pre>import Data.Foldable1 (Foldable1, foldMap1)<br>import Data.Semigroup (First (..))<br><br>data EWMAUpdate = EWMAUpdate<br>  { ewmaAlpha :: Double,<br>    ewmaTarget :: Double<br>  }<br>  deriving (Eq, Show)<br><br>instance Semigroup EWMAUpdate where<br>  EWMAUpdate a1 v1 &lt;&gt; EWMAUpdate a2 v2 =<br>    EWMAUpdate newAlpha newTarget<br>    where<br>      newAlpha = a1 + a2 - a1 * a2<br>      newTarget<br>        | newAlpha == 0 = 0<br>        | otherwise = (a1 * v1 + a2 * v2 - a1 * a2 * v2) / newAlpha<br><br>instance Monoid EWMAUpdate where<br>  mempty = EWMAUpdate 0 0<br><br>data EWMA = EWMA<br>  { ewmaPrior :: First Double,<br>    ewmaUpdate :: EWMAUpdate<br>  }<br>  deriving (Eq, Show)<br><br>instance Semigroup EWMA where<br>  EWMA p1 u1 &lt;&gt; EWMA p2 u2 = EWMA (p1 &lt;&gt; p2) (u1 &lt;&gt; u2)<br><br>singleEWMA :: Double -&gt; Double -&gt; EWMA<br>singleEWMA alpha x = EWMA (First x) (EWMAUpdate alpha x)<br><br>ewmaValue :: EWMA -&gt; Double<br>ewmaValue (EWMA (First x) (EWMAUpdate a v)) = (1 - a) * x + a * v<br><br>ewma :: (Foldable1 t) =&gt; Double -&gt; t Double -&gt; Double<br>ewma alpha = ewmaValue . foldMap1 (singleEWMA alpha)</pre><p>An EWMAUpdate is effectively a function of the form above, and its semigroup instance is reversed function composition. However, it is stored in a defunctionalized form so that the composition can be computed in advance. Then we add a few helper functions: singleEWMA to compute the EWMA for a single data point (with a given α value), ewmaValue to apply the update function to the prior and produce an estimate, and ewma to use this semigroup to compute the EWMA of any non-empty sequence.</p><h4>Why?</h4><p>What have we gained by looking at the problem this way?</p><p>The standard answer is that we’ve gained flexibility in how we perform the computation. In addition to computing an EWMA sequentially from left to right, we can do things like:</p><ul><li>Compute the EWMA in a parallel or distributed way, farming out parts of the work to different threads or machines.</li><li>Compute an EWMA on the fly in the intermediate nodes of a balanced tree, such as one can do with Haskell’s <a href="https://hackage.haskell.org/package/fingertree">fingertree</a> package.</li></ul><p>But neither of these are the reason I worked this out today. Instead, I was interested in a continuous time analogue of the EWMA. The traditional form of the EWMA is recursive, which gives you a value after each whole step of recursion, but doesn’t help much with continuous time. By working out this semigroup structure, the durations of each observation, which previously were implicit in the depth of recursion, turn into explicit weights that accumulate as different time intervals are concatenated. It’s then easy to see how you might incorporate a continuous observation with a non-unit duration, or with greater or lesser weight for some other reason.</p><p>That’s not new, of course, but it’s nice to be able to work it out simply by searching for algebraic structure.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=860a3e28409d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>