<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>John D. Cook</title>
	<atom:link href="http://www.johndcook.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.johndcook.com/blog</link>
	<description>Applied Mathematics Consulting</description>
	<lastBuildDate>Tue, 11 Aug 2026 14:32:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.johndcook.com/wp-content/uploads/2020/01/cropped-favicon_512-32x32.png</url>
	<title>John D. Cook</title>
	<link>https://www.johndcook.com/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Dogs and fat tails</title>
		<link>https://www.johndcook.com/blog/2026/08/11/dogs-and-fat-tails/</link>
					<comments>https://www.johndcook.com/blog/2026/08/11/dogs-and-fat-tails/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 14:09:46 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Power laws]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247616</guid>

					<description><![CDATA[<p>I was reading a blog post on boat names because it was on Hacker News this morning. The post contained a link to a data set on dog names in NYC and I poked around the data a little. The top names were not at all what I expected, but then again this is limited [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/11/dogs-and-fat-tails/">Dogs and fat tails</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I was reading a blog post on boat names because it was on Hacker News this morning. The post contained a link to a data set on <a href="https://data.cityofnewyork.us/Health/NYC-Dog-Licensing-Dataset/nu7n-tubp/about_data?ref=beautifulpublicdata.com">dog names in NYC</a> and I poked around the data a little. The top names were not at all what I expected, but then again this is limited to NYC; it&#8217;s not a sample across the US. These were the top 10 names:</p>
<ol>
<li>Bella</li>
<li>Luna</li>
<li>Max</li>
<li>Charlie</li>
<li>Coco</li>
<li>Lola</li>
<li>Rocky</li>
<li>Milo</li>
<li>Teddy</li>
<li>Lucy</li>
</ol>
<p>I wondered if the name frequencies might fit a power-law distribution. They do not, but they follow a log-normal distribution remarkably well.</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/dog_name_frequency.png" width="1100" height="800" /></p>
<h2>Related posts</h2>
<ul>
<li class='link'><a href='https://www.johndcook.com/blog/2009/04/16/metabolism-and-power-laws/'>Metabolism and power laws</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2015/03/09/why-isnt-everything-normally-distributed/'>Why isn&#8217;t everything normally distributed?</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2009/08/19/generalized-central-limit-theorem/'>Power laws and the generalized CLT</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/08/11/dogs-and-fat-tails/">Dogs and fat tails</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/11/dogs-and-fat-tails/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Manually unbreakable cryptography</title>
		<link>https://www.johndcook.com/blog/2026/08/11/manually-unbreakable-cryptography/</link>
					<comments>https://www.johndcook.com/blog/2026/08/11/manually-unbreakable-cryptography/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 12:04:24 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247608</guid>

					<description><![CDATA[<p>Suppose you were able to go back in time, to an era before computers, and give someone contemporary cryptography. Encryption methods that are essentially unbreakable now would certainly be unbreakable then. But there&#8217;s a catch: not only do attackers not have computers, neither do users. Manual cryptography If you told someone about RSA encryption, for [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/11/manually-unbreakable-cryptography/">Manually unbreakable cryptography</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Suppose you were able to go back in time, to an era before computers, and give someone contemporary cryptography. Encryption methods that are essentially unbreakable now would certainly be unbreakable then. But there&#8217;s a catch: not only do attackers not have computers, neither do users.</p>
<h2>Manual cryptography</h2>
<p>If you told someone about RSA encryption, for example, you&#8217;d lose them right after you said &#8220;First you find a couple 1000-digit primes.&#8221; But there&#8217;s no need for using 1000-digit primes if no attacker has a computer. You could use 100 digit primes. Could you use 10 digit primes? If you chose primes just big enough to make the method unbreakable by hand, could someone implement it by hand?</p>
<p>Kirchoff&#8217;s principle says the strength of an encryption method should depend only on keeping the key secret, not the method. If you <em>could</em> keep the method secret, RSA would be unbreakable because nobody thought of anything like it before computers. But to make our thought experiment more interesting, let&#8217;s suppose that an enemy has also traveled back in time. If you tell your side about RSA, he can tell his side about it as well. So we&#8217;re back to Kirchoff&#8217;s principle.</p>
<p>An encryption method combinining substitution and permutation would have been practical to carry our manually. The <a href="https://www.johndcook.com/blog/2020/02/22/adfgvx/">ADFGVX</a> cipher from 1918 was a start in this direction. That idea could been extended further, with a larger substitution set and longer permutations, and with more than one round of substitution and permutation, approaching what would be come the approach used in modern symmetric encryption. Such a method might have been manually implementable without being manually breakable.</p>
<h2>Mechanized cryptography</h2>
<p>World War II was a time of transition from manual cryptography to computerized cryptography. Encryption machines were attacked by cryptanalysis machines, though these machines were general-purpose computers. If you could implement a symmetric encryption method like AES in a mechanical device, no mechanical device could break it.</p>
<p>You could use something like <a href="https://www.johndcook.com/blog/2020/04/04/feistel-networks/">DES</a>, simpler than AES but still unbreakable at the time. DES is considered broken because now you could throw enough compute power at it to break it by brute force, but that would not be possible with only mechanical devices.</p>
<p>My hunch is that the best approach would be stream ciphers. Maybe it would be practical to implement one of these by hand or with the aid of simple calculating machines. Something like PCG, which is not cryptographically secure today [1], would have been then, though I don&#8217;t know how practical it would have been to carry out PCG, say, in the 1940s.</p>
<h2>More pre-computer cryptography</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2021/08/19/vigenere/">Vigenère cipher</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2023/02/27/playfair-cipher/">Playfair cipher</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2024/01/25/straddling-checkerboard-encryption/">Straddling checkerboard</a></li>
</ul>
<p>[1] In 2020, Charles Bouillaguet, Florette Martinez,<span class="s1"> </span>and Julia Sauvage were able to break PCG using 20,000 CPU-hours. See their paper Practical seed-recovery for the PCG Pseudo-Random Number Generator. IACR Transactions on Symmetric Cryptology. ISSN 2519-173X, Vol. 2020, No. 3, pp. 175–196.</p>The post <a href="https://www.johndcook.com/blog/2026/08/11/manually-unbreakable-cryptography/">Manually unbreakable cryptography</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/11/manually-unbreakable-cryptography/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Learning from historical mistakes</title>
		<link>https://www.johndcook.com/blog/2026/08/10/learning-from-historical-mistakes/</link>
					<comments>https://www.johndcook.com/blog/2026/08/10/learning-from-historical-mistakes/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 14:34:21 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247605</guid>

					<description><![CDATA[<p>The following extraordinary paragraph comes from Knuth&#8217;s TAOCP Volume 4A, right before the last set of exercises. Many of the exercises below ask a modern reader to find and/or to correct errors in the literature of bygone days. The point is not to gloat over how smart we are in the 21st century; the point [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/10/learning-from-historical-mistakes/">Learning from historical mistakes</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The following extraordinary paragraph comes from Knuth&#8217;s TAOCP Volume 4A, right before the last set of exercises.</p>
<blockquote><p>Many of the exercises below ask a modern reader to find and/or to correct errors in the literature of bygone days. The point is not to gloat over how smart we are in the 21st century; the point is rather to understand that even the pioneers of a subject can stumble. One good way to learn that a set of ideas is not really as simple as it might seem to today&#8217;s computer scientists and mathematicians is to observe that some of the world&#8217;s leading thinkers had to struggle with the concepts when they were new.</p></blockquote>The post <a href="https://www.johndcook.com/blog/2026/08/10/learning-from-historical-mistakes/">Learning from historical mistakes</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/10/learning-from-historical-mistakes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Inverse differential equations</title>
		<link>https://www.johndcook.com/blog/2026/08/10/inverse-differential-equations/</link>
					<comments>https://www.johndcook.com/blog/2026/08/10/inverse-differential-equations/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 14:32:42 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<category><![CDATA[Special functions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247603</guid>

					<description><![CDATA[<p>In science and engineering classes, you might describe a system using Newton&#8217;s laws and end up with a differential equation. You then solve the differential equation, analytically or numerically, to see how the solutions behave. You might also do the opposite, especially in a mathematics class: look at what differential equation a set of functions [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/10/inverse-differential-equations/">Inverse differential equations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>In science and engineering classes, you might describe a system using Newton&#8217;s laws and end up with a differential equation. You then solve the differential equation, analytically or numerically, to see how the solutions behave.</p>
<p>You might also do the opposite, especially in a mathematics class: look at what differential equation a set of functions satisfy in order to understand those functions.</p>
<p>Bessel functions came out of solving differential equations from <a href="https://www.johndcook.com/blog/2026/06/06/from-kepler-to-bessel/">astronomy</a>. But then they turned out to be useful, not just in other areas of science, but in pure mathematics as well. So there are Bessel function users who came to the functions first and haven&#8217;t seen the differential equation they came from.</p>
<p>You can learn a lot about Bessel functions, and other special functions, by looking at their defining differential equation even if you&#8217;re not directly interested in the differential equation or the physical problem that motivated it.</p>
<p>Bessel functions satisfy</p>
<p style="padding-left: 40px;"><em>x</em>² <em>y</em>″ + <em>x</em> <em>y</em>′ + (<em>x</em>² − <em>n</em>²) <em>y</em> = 0.</p>
<p>You can tell a lot about Bessel functions just by inspecting this equation without solving it. If we divide by <em>x</em>² and write the equation in the form</p>
<p style="padding-left: 40px;"><em>y</em>″ + (<em>p</em>(<em>x</em>)/<em>x</em>) <em>y</em>′ + (<em>q</em>(<em>x</em>)/<em>x</em>²) <em>y </em>= 0</p>
<p>then <em>p</em>(<em>x</em>) = 1 and <em>q</em>(<em>x</em>) = <em>x</em>² − <em>n</em>². The indicial equation</p>
<p style="padding-left: 40px;"><em>r</em>(<em>r</em> − 1) + <em>p</em>(0) <em>r</em> + <em>q</em>(0) = 0</p>
<p>reduces to</p>
<p style="padding-left: 40px;"><em>r</em>² = <em>n</em>²</p>
<p>and so <em>r</em> = ±<em>n</em>. That alone tells us there are two solutions, one analytic at zero and one singular at zero. These are <em>J</em><sub><em>n</em></sub> and <em>Y</em><sub><em>n</em></sub> respectively. It also tells us the behavior of these functions as <em>x</em> goes to zero and as <em>x</em> goes to infinity. To find out more, look up &#8220;method of Frobinius.&#8221;</p>The post <a href="https://www.johndcook.com/blog/2026/08/10/inverse-differential-equations/">Inverse differential equations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/10/inverse-differential-equations/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DNA and Bessel functions</title>
		<link>https://www.johndcook.com/blog/2026/08/09/dna-and-bessel-functions/</link>
					<comments>https://www.johndcook.com/blog/2026/08/09/dna-and-bessel-functions/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 09 Aug 2026 17:17:42 +0000</pubDate>
				<category><![CDATA[Science]]></category>
		<category><![CDATA[Math]]></category>
		<category><![CDATA[Special functions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247593</guid>

					<description><![CDATA[<p>I was reading a book on the history of the discovery of the structure of DNA [1] and was surprised by a few passing references to Bessel functions. According to Claude, When X-rays are diffracted by a helical structure, the resulting diffraction pattern breaks into a series of horizontal &#8220;layer lines.&#8221; Cochran, Crick, and Vand [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/09/dna-and-bessel-functions/">DNA and Bessel functions</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I was reading a book on the history of the discovery of the structure of DNA [1] and was surprised by a few passing references to Bessel functions.</p>
<p>According to Claude,</p>
<blockquote><p>When X-rays are diffracted by a helical structure, the resulting diffraction pattern breaks into a series of horizontal &#8220;layer lines.&#8221; Cochran, Crick, and Vand showed mathematically that the diffracted amplitude on the <em>n</em>-th layer line is proportional to a Bessel function of the first kind, order <em>n</em>:</p>
<p style="padding-left: 40px;"><em>J</em><sub><em>n</em></sub>(2π <em>r R</em>)</p>
<p>where <em>r</em> is the radius of the helix and <em>R</em> is the distance out from the center (the meridian) in the diffraction pattern.</p></blockquote>
<p>The citation for this paragraph is a paper from 1952 [2] that amazingly is behind a paywall.</p>
<p><strong>Update</strong>: Evan pointed out something fishy in the expression above: &#8220;Isn’t there a dimensional problem with <em>J</em>(2π <em>r</em> <em>R</em>)? Maybe 2π<em>r</em>/<em>R</em> instead?&#8221;</p>
<p>This was Claude&#8217;s response:</p>
<ul>
<li><strong>r</strong> is the radius of the helix, a real-space length (say, in Å).</li>
<li><strong>R</strong> is the radial coordinate in <strong>reciprocal space</strong> — it has units of inverse length (Å⁻¹), because that&#8217;s how X-ray diffraction/Fourier space works (spatial frequency, not distance).</li>
</ul>
<p>So Evan and Claude were both right. There is an implicit division happening, but crystallographers do write <em>r R</em>.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2026/06/05/mr-bessels-eponymous-functions/">Mr. Bessel’s eponymous functions</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/06/06/from-kepler-to-bessel/">From Kepler to Bessel</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2024/01/31/bessel-zero-spacing/">Bessel zero spacing</a></li>
</ul>
<p>[1] Watson and Crick didn&#8217;t &#8220;discover DNA&#8221; as is commonly said. DNA was discovered in 1878. Watson and Crick discovered the <em>structure</em> of DNA in 1953.</p>
<p>[2] Cochran, W., Crick, F. H. C., &amp; Vand, V. (1952). &#8220;The Structure of Synthetic Polypeptides. I. The Transform of Atoms on a Helix.&#8221; Acta Crystallographica, 5(5), 581–586.</p>The post <a href="https://www.johndcook.com/blog/2026/08/09/dna-and-bessel-functions/">DNA and Bessel functions</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/09/dna-and-bessel-functions/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>A simple range reduction method</title>
		<link>https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/</link>
					<comments>https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/#respond</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 09 Aug 2026 16:46:59 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247580</guid>

					<description><![CDATA[<p>At the end of my post on how not to calculate cosine I said that the first step in calculating cosine, particularly cosine of a large number, would be to do range reduction. This post will present a simple range reduction method by Cody and Waite that is adequate for moderately large arguments. If you [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/">A simple range reduction method</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>At the end of my post on <a href="https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/">how not to calculate cosine</a> I said that the first step in calculating cosine, particularly cosine of a large number, would be to do range reduction. This post will present a simple range reduction method by Cody and Waite that is adequate for moderately large arguments.</p>
<p>If you want to compute the sine or cosine of an angle <em>x</em> you could start by reducing <em>x</em> mod 2π since that would not change the result. However, accurately reducing a number mod 2π is not trivial; that&#8217;s why range reduction is an area of algorithm development.</p>
<h2>Range reduction mod π/2</h2>
<p>Even better would be to reduce <em>x</em> mod π/2. Reducing to a smaller range means that power series method, and other methods such as rational approximation, will be more efficient.</p>
<p>So suppose you can find an integer <em>k</em> such that</p>
<p style="padding-left: 40px;"><em>x</em> − <em>k</em> π/2 = <em>y</em></p>
<p>where 0 ≤ <em>y</em> ≤ π/2. Then sin(<em>x</em>) is ±sin(<em>y</em>) or ±cos(<em>y</em>), depending on <em>k</em> mod 4 equals 0, 1, 2, or 3.</p>
<pre>from math import *

def reduced_sin(x, k):
     match k % 4:
        case 0: return sin(x)
        case 1: return cos(x)
        case 2: return -sin(x)
        case 3: return -cos(x)
</pre>
<h2>Naive range reduction</h2>
<p>Now let&#8217;s set <em>x</em> = 500. Then <em>k</em> = 318 because that&#8217;s the multiple of π/2 we need to subtract to bring <em>x</em> into range, and the sine of <em>x</em> should be the negative of the sine of the reduced value <em>y</em> because 318 = 2 mod 4.</p>
<p>The following code computes sin(<em>x</em>) with naive range reduction</p>
<pre>def naive_sin(x):
    k = floor(x / (pi/2))
    y = x % (pi/2)
    return reduced_sin(y, k)
</pre>
<p>and when <em>x</em> = 500 the error is on the order of 1.7 × 10<sup>−14</sup>.</p>
<h2>Better range reduction</h2>
<p>The value of <em>k</em> above is fine, but we&#8217;d like to calculate <em>y</em> more accurately. The following code is much better.</p>
<pre>def Cody_Waite_sin(x):
    C1 = 1686629713 / 2**30
    C2 = 4701928774853425 / 2**86

    k = floor(x / (pi/2))
    y = (x - k*C1) - k*C2
    return reduced_sin(y, k)
</pre>
<p>This will compute sin(500) to full machine precision. What kind of magic is this?</p>
<p>The trick is that the exact value of C1 + C2 equals π/2 to more precision than is possible in a single float [1]. You can confirm, with <em>bc</em> or some other extended precision software, that the difference between C1 + C2 and π/2 is roughly 2<sup>−88</sup>, while the limit of float precision is 2<sup>−52</sup>.</p>
<p>If we compute</p>
<pre>y = x - k*(C1 + C2)</pre>
<p>then we&#8217;re doing the same calculation as <code>naive_sin</code> and will get the same error. But if we compute</p>
<pre>y = (x - k*C1) - k*C2</pre>
<p>we will get a more accurate result, provided <em>x</em> isn&#8217;t too large.</p>
<p>You can use the following code to play around and see how large <em>x</em> can be before errors start to creep in. For small enough <em>x</em>, like 500, the Cody and Waite sine returns full precision. For larger <em>x</em> it&#8217;s better than naive sine but does not return full precision. And for large enough <em>x</em> it completely breaks down.</p>
<pre>def compare(x):
    y0 = naive_sin(x) 
    y1 = Cody_Waite_sin(x)
    y2 = sin(x)
    print("Naive error:     ", y2 - y0)
    print("Cody Waite error:", y2 - y1)
</pre>
<p>Now this may seem circular since we&#8217;re using <code>math.sin</code> as our gold standard. However, this function is calling the sine function on your CPU, which is using sophisticated range reduction to compute its result accurately down to the last bit, assuming you run the code on a computer that&#8217;s less than 40 years old.</p>
<p>The Cody and Waite algorithm is inadequate for large <em>x</em>, but it&#8217;s a good place to begin studying range reduction. It shows there are clever ways of squeezing out more precision than seems possible.</p>
<p>&nbsp;</p>
<p>[1] The numerator <em>n</em><sub>1</sub> of C1 is ⌊2<sup>30</sup> π/2⌋. The numerator <em>n</em><sub>2</sub> of C2 is the solution to</p>
<p style="padding-left: 40px;">2<sup>86−30</sup> <em>n</em><sub>1</sub> + <em>n</em><sub>2</sub> = ⌊2<sup>86</sup> π/2⌋.</p>The post <a href="https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/">A simple range reduction method</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Corrupted apostrophes</title>
		<link>https://www.johndcook.com/blog/2026/08/07/corrupted-apostrophes/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 08 Aug 2026 01:20:25 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Unicode]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247576</guid>

					<description><![CDATA[<p>I have a program that shares files between my laptop and my phone. It works well, except for apostrophes. When I type an apostrophe ' on my laptop, it becomes â€&#x2122; on my phone. And when I type 's on my phone, it becomes 痴 on my laptop. Apparently the phone turns the apostrophe (U+0027) [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/07/corrupted-apostrophes/">Corrupted apostrophes</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I have a program that shares files between my laptop and my phone. It works well, except for apostrophes.</p>
<p>When I type an apostrophe <code>'</code> on my laptop, it becomes <code>â€&#x2122;</code> on my phone. And when I type <code>'s</code> on my phone, it becomes <code>痴</code> on my laptop.</p>
<p>Apparently the phone turns the apostrophe (U+0027) into a right single quote (U+2019), then bungles bytes in the UTF-8 encoding of U+2019 as three Windows-1252 characters. The bytes E28099<sub>hex</sub> are interpreted as <code>â</code> (E2<sub>hex</sub>), <code>€</code> (80<sub>hex</sub>), and <code>&#x2122;</code> (99<sub>hex</sub>).</p>
<p>When I type <code>'s</code> on my phone, it is encoded as two Windows-1252 characters 92<sub>hex</sub> and 73<sub>hex</sub>. Then by the time the text appears on my laptop, the bytes 9273<sub>hex</sub> are interpreted as a Shift-JIS encoding of the CJK character <code>痴</code> (U+75F4).</p>
<p>Here&#8217;s Python code to reproduce the problem.</p>
<pre>
def mojibake(s: str, n: int, bad_encoding: str = 'cp1252') -> str:
    for _ in range(n):
        s = s.encode('utf-8').decode(bad_encoding, errors='replace')
    return s

print(mojibake("’s", 1))
print(mojibake("’s", 1, 'shift_jis'))
</pre>
<p>The code won&#8217;t corrupt ASCII text. The problem started with an ASCII character being replaced by a similar non-ASCII character. Text containing a non-ASCII character gets more corrupted with each round.</p>The post <a href="https://www.johndcook.com/blog/2026/08/07/corrupted-apostrophes/">Corrupted apostrophes</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How not to calculate cosine</title>
		<link>https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/</link>
					<comments>https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Fri, 07 Aug 2026 15:11:10 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247572</guid>

					<description><![CDATA[<p>Calculus professors with no experience in numerical computing will tell students that computers calculate trig functions with power series. They don&#8217;t. I worked on the implementation of trig functions in hardware, and I can assure you we didn&#8217;t just use power series. Power series are an excellent way to calculate functions near the center of [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/">How not to calculate cosine</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Calculus professors with no experience in numerical computing will tell students that computers calculate trig functions with power series. They don&#8217;t. I worked on the implementation of trig functions in hardware, and I can assure you we didn&#8217;t just use power series.</p>
<p>Power series are an excellent way to calculate functions <em>near the center of the series</em>, such as computing <a href="https://www.johndcook.com/blog/2010/07/27/sine-approximation-for-small-x/">sine for small angles</a>. But the further you get from the center, the less useful power series are.</p>
<p>Let&#8217;s suppose you want to calculate cos(200) using the power series for cosine. The <em>n</em>th term of that series is</p>
<p style="padding-left: 40px;">(−1)<sup><em>n</em></sup> <em>x</em><sup>2<em>n</em></sup> / (2<em>n</em>)!</p>
<p>This is an alternating series, and so the error in truncating the series after <em>n</em> terms is bounded by the size of the <em>n</em>+1 term, <em>if</em> you&#8217;ve gone far enough out in the series that the terms are monotonically decreasing in absolute value.</p>
<p>To calculate cos(200) to machine precision, i.e. with an error of less than 2<sup>−52</sup>, we&#8217;d need to sum the series up to <em>n</em> where</p>
<p style="padding-left: 40px;">| 200<sup>2<em>n</em>+2</sup> / (2<em>n</em> + 2)! | &lt; 2<sup>−52</sup></p>
<p>Actually, that will ensure that the <em>absolute</em> error is small enough, but not that the <em>relative</em> error is small enough; if the value of cos(200) is small, we&#8217;d need more terms. Let&#8217;s ignore that and assume we&#8217;re only concerned with absolute error.</p>
<p>Turns out we&#8217;d need 287 terms. That&#8217;s a lot of terms. But you might say &#8220;That&#8217;s fine. I&#8217;m not in a hurry, and it&#8217;s just more work for the computer, not for me.&#8221; OK, so let&#8217;s try.</p>
<pre>from math import *

s = 0
for n in range(288):
    s += (-1)**n * 200**(2*n) / factorial(2*n)
print(s)
</pre>
<p>This prints -3.6840358571084123e+67. You may suspect the answer is incorrect since values of cosine are on the order of 1, not on the order of 10<sup>67</sup>. Something went spectacularly bad. On closer inspection, it&#8217;s remarkable the code didn&#8217;t crash.</p>
<p>If you changed <code>200</code> to <code>200.0</code> above, the code would crash. Calculating <code>200.0**(2*n)</code> overflows when <em>n</em> = 67. But when we calculate <code>200**(2*n)</code>, the result is an integer. And we&#8217;re dividing by <code>factorial(2*n)</code>, which is also an integer. Both of these integers become too large to fit in a float, but their <em>ratio</em> has a maximum value of around 10<sup>80</sup>, smaller than the maximum float, which is on the order of 10<sup>308</sup>.</p>
<p>When we don&#8217;t overflow, we have a different problem: catastrophic cancellation. You can&#8217;t calculate a number between −1 and 1 as an alternating sum of numbers as large as 10<sup>80</sup>. You&#8217;d need more than 80 + 16 = 96 decimal places of precision to compute the sum accurately, and floating point only gives you between 15 and 16 decimal places of precision.</p>
<p>So how <em>would</em> you calculate cos(200)? The first step would be to use some sort of range reduction on 200. You could reduce 200 mod 2π to get a smaller number to work with.</p>
<pre>&gt;&gt;&gt; from math import cos, pi
&gt;&gt;&gt; x = 200 % (2*pi)
&gt;&gt;&gt; x
5.221255477432827
</pre>
<p>Using a power series to compute the cosine of 5.221255477432827 is feasible, but not optimal. There&#8217;s also another problem: the naive range reduction above loses some precision.</p>
<pre>&gt;&gt;&gt; cos(x)
0.48718767500701254
&gt;&gt;&gt; cos(x) - cos(200)
6.661338147750939e-15
</pre>
<p>The error is small, but it&#8217;s still an order of magnitude larger than machine precision. You can&#8217;t simply reduce <em>n</em> mod 2π with ordinary float division because the integer part of <em>n</em> / 2π pushes some digits of precision off the right end. I intend to write about how range reduction works in future posts.</p>
<p><strong>Update</strong>: See <a href="https://www.johndcook.com/blog/2026/08/09/simple-range-reduction/">this post</a> for a simple range reduction algorithm that is fine for values of <em>x</em> such as 200, but not adequate for much larger values.</p>The post <a href="https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/">How not to calculate cosine</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/07/how-not-to-calculate-cos/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>cos(200!)</title>
		<link>https://www.johndcook.com/blog/2026/08/07/cos200/</link>
					<comments>https://www.johndcook.com/blog/2026/08/07/cos200/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Fri, 07 Aug 2026 13:19:07 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247561</guid>

					<description><![CDATA[<p>In a footnote to the previous post, I said that Python&#8217;s math library can calculate the logarithm of extremely large numbers but not the cosine. This post will expand on that comment. In this post I&#8217;ll use n = 200! as my example rather than 1000! because this value of N is larger than the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/07/cos200/">cos(200!)</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>In a footnote to the <a href="https://www.johndcook.com/blog/2026/08/06/log1000/">previous post</a>, I said that Python&#8217;s math library can calculate the logarithm of extremely large numbers but not the cosine. This post will expand on that comment.</p>
<p>In this post I&#8217;ll use <em>n</em> = 200! as my example rather than 1000! because this value of <em>N</em> is larger than the largest representable floating point number but small enough to be more convenient to work with.</p>
<p>Suppose someone calculates 200! for you:</p>
<pre>78865786736479050355236321393218506229513597768717326329474253324435\
94499634033429203042840119846239041772121389196388302576427902426371\
05061926624952829931113462857270763317237396988943922445621451664240\
25403329186413122742829485327752424240757390324032125740557956866022\
60319041703240623517008587961789222227896237038973747200000000000000\
00000000000000000000000000000000000
</pre>
<p>You could now calculate log(<em>n</em>) using</p>
<p style="padding-left: 40px;"><em>n</em> = 7.886578673647905 × 10<sup>374</sup></p>
<p>and so</p>
<p style="padding-left: 40px;">log(<em>n</em>) = log(7.886578673647905 × 10<sup>374</sup>)<br />
= log(7.886578673647905) + 374 log(10) = 863.2319871924055.</p>
<p>The key thing that makes this possible is that the least significant digits of <em>n</em> only affect the least significant digits of log(<em>n</em>). In the calculation above I kept the first 16 digits of <em>n</em>. Python couldn&#8217;t make use of any more digits, and had no need of any more digits, in order to produce the logarithm to machine precision.</p>
<p>Cosine doesn&#8217;t work that way. The cosine of <em>n</em> depends on the remainder when <em>n</em> is divided by 2π, and that remainder depends on every single digit of <em>n</em>. I&#8217;ll illustrate that below.</p>
<p>Using <code>bc -l</code> and setting the scale to 400, I can calculated <em>n</em> then calculate</p>
<p style="padding-left: 40px;">cos(<em>n</em> + 10<sup><em>i</em></sup>)</p>
<p>for i running from 0 to 374, tweaking each digit one at a time. (Except when a digit is a 9 and the addition results in a carry.)</p>
<pre>    n = 1
    for (i = 1; i &lt;= 200; i++) n *= i
    scale = 400
    for (i = 1; i &lt;= 374; i++) {
        x = c(n+10^i)
        scale = 16
        print x/1, "\n"
        scale = 400
    }
</pre>
<p>Here&#8217;s what a plot of the results look like.</p>
<p><img decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/cos200factorial1.png" width="480" height="360" /></p>
<p>The value of cos(<em>n</em>) is about −0.985, but the values above are all over the map. We can look at the range by projecting all the points over to the left edge then rotating a quarter turn:</p>
<p><img decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/cos200factorial2.png" width="360" height="15" /></p>
<p>The remarkable thing about this image is that there are a few gaps, i.e. a few values the cosine does <em>not</em> take on.</p>
<p>Here&#8217;s a more sophisticated way to look at it. The sequence 10<sup><em>i</em></sup> mod 2π is dense in [0, 2π], and so by going far enough out in the sequence, we can find a value that shifts the phase of <em>n</em> by any desired amount within any given tolerance.</p>
<p>Every digit in <em>n</em> matters, and changing any digit can change the value of cosine to be essentially any value. You cannot calculate the cosine of an enormous number without using some kind of extended precision arithmetic. There are clever range reduction algorithms that minimize the amount of extended arithmetic necessary, but extended arithmetic cannot be completely eliminated.</p>The post <a href="https://www.johndcook.com/blog/2026/08/07/cos200/">cos(200!)</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/07/cos200/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Calculating log(1000!)</title>
		<link>https://www.johndcook.com/blog/2026/08/06/log1000/</link>
					<comments>https://www.johndcook.com/blog/2026/08/06/log1000/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 06 Aug 2026 13:23:43 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247553</guid>

					<description><![CDATA[<p>The previous post pointed out that the following code such as the following unexpectedly works. &#62;&#62;&#62; from math import log, factorial &#62;&#62;&#62; log(factorial(1000)) 5912.128178488163 If you don&#8217;t find this unexpected, note that if you replace math.log with numpy.log the code will fail [1]. Functions like natural logarithm operate on real numbers. Real numbers are represented [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/06/log1000/">Calculating log(1000!)</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://www.johndcook.com/blog/2026/08/05/math-log/">previous post</a> pointed out that the following code such as the following unexpectedly works.</p>
<pre>&gt;&gt;&gt; from math import log, factorial
&gt;&gt;&gt; log(factorial(1000))
5912.128178488163
</pre>
<p>If you don&#8217;t find this unexpected, note that if you replace <code>math.log</code> with <code>numpy.log</code> the code will fail [1]. Functions like natural logarithm operate on real numbers. Real numbers are represented as floating point numbers in programming languages, and 1000! factorial is too large to represent as a standard floating point number. (More on that <a href="https://www.johndcook.com/blog/2009/04/06/anatomy-of-a-floating-point-number/">here</a>.)</p>
<p>In this post I&#8217;d like to look at how you might calculate log(1000!) with less capable software, and even without software.</p>
<p>One approach would be to sum the logarithms of the numbers 1 through 1000. This will give essentially the same result as above, with a little difference in the last couple decimal places due to rounding error.</p>
<p>If you have a way to calculate 1000! but not a way to cast it to a floating point number, you could do this manually.</p>
<pre>&gt;&gt;&gt; s = str(factorial(1000))
&gt;&gt;&gt; s[:16]
'4023872600770937'
&gt;&gt;&gt; len(s)
2568
</pre>
<p>This tells us 1000! = 4.023872600770937 × 10<sup>2567</sup>. Therefore</p>
<p style="padding-left: 40px;">log(1000!) = log(4.023872600770937) + 2567 log(10)</p>
<p>which only requires working with numbers of modest size.</p>
<h2>Calculating by hand</h2>
<p>Now suppose it&#8217;s 1964. You don&#8217;t have a computer, or even a calculator, but you do have a copy of the recently published Handbook of Mathematical Functions by Abramowitz and Stegun (A&amp;S). You turn to Table 6.6 &#8220;Factorials for large arguments.&#8221; This has values of factorial for 100, 200, 300, …, 1000, so you can simply look up your answer to 20 decimal places.</p>
<p>That was too easy; I didn&#8217;t expect that to be there when I started writing this post. If you wanted to compute log(950!), for example, you&#8217;d have to work harder. You could find A&amp;S equation 6.1.41 (Stirling&#8217;s series) which says</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/AS6.1.41.svg" alt="\begin{align*} \ln \Gamma(z) &amp;\sim (z - \tfrac{1}{2}) \ln z - z + \tfrac{1}{2} \ln 2\pi + \frac{1}{12z} - \frac{1}{360z^3} \\ &amp;+ \frac{1}{1260z^5} - \frac{1}{680z^7} + \cdots \end{align*}" width="401" height="90" /></p>
<p>So how would you use this formula to calculate log(1000!)? Since <em>n</em>! = Γ(<em>n</em> + 1), you set <em>z</em> = 1001.</p>
<p>You&#8217;d need to decide how many terms you need to use. Assuming the error is on the order of the first term you leave out, you&#8217;d reason that you could probably stop with the 1/12<em>z</em> term because the next term is between 10<sup>−11</sup> and 10<sup>−12</sup>.</p>
<p>You find Table 4.2 has natural logarithms, but not for 1001. You can look up log(1.001), however, and at the bottom of the same page is log(10) to 16 decimal places, and you can find log(10) to 24 decimal places in Table 1.1. So you calculate</p>
<p style="padding-left: 40px;">log(1001) = log(1.001 × 10³) = log(1.001) + 3 log(10).</p>
<p>You can find log(2) and log(π) in Table 1.1, and average them to find ½ log(2π).</p>
<p>Here&#8217;s Python code to simulate the hand calculations.</p>
<pre>log2     = 0.6931_47180_55994_53094_172321 # Table 1.1
log10    = 2.3025_85092_99404_56840_179915 # Table 1.1
logpi    = 1.1447_29885_84940_01741_43427  # Table 1.1
log1_001 = 0.00099_95003_330835            # Table 4.2

z = 1001
logz = log1_001 + 3*log10
s = (z - 0.5)*logz - z + (log2 + logpi)/2 + 1/(12*z)

print(s)
</pre>
<p>This result differs from the one at the top of the post only in the last decimal place.</p>
<h2>Related posts</h2>
<p>Doing calculations with tables is not as simple as &#8220;just look it up.&#8221; It takes a bit of skill.</p>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2024/06/03/using-a-table-of-logarithms/">Using a table of logarithms</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2024/06/25/trig-tables/">Using a trig table</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/03/26/table-precision/">How much precision can you squeeze out of a table?</a></li>
</ul>
<p>[1] The code will also fail if you replace <code>math.log</code> with <code>math.cos</code>. Both logarithm and cosine return moderate sized real numbers when given enormous inputs like 1000!, so representing the output as a float is not the problem. But logarithms of huge numbers can be computed with ordinary precision functions, as above. But computing the cosine of a huge number requires extended precision.</p>
<p><strong>Update</strong>: The <a href="https://www.johndcook.com/blog/2026/08/07/cos200/">next post</a> expands on why computing the cosine of a large number is more difficult than computing the log.</p>The post <a href="https://www.johndcook.com/blog/2026/08/06/log1000/">Calculating log(1000!)</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/06/log1000/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>The code that didn&#8217;t break</title>
		<link>https://www.johndcook.com/blog/2026/08/05/math-log/</link>
					<comments>https://www.johndcook.com/blog/2026/08/05/math-log/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 05 Aug 2026 18:26:25 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247547</guid>

					<description><![CDATA[<p>Last week I wrote a post on hiding cryptographic keys in decks of cards. I wrote some code for that post that shouldn&#8217;t work, but before fixing I noticed that it in fact did work. The code computes logarithms for integers larger than the largest representable float. For example, the largest float is on the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/05/math-log/">The code that didn’t break</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Last week I wrote a <a href="https://www.johndcook.com/blog/2026/07/28/keys-and-cards/">post</a> on hiding cryptographic keys in decks of cards. I wrote some code for that post that shouldn&#8217;t work, but before fixing I noticed that it in fact did work.</p>
<p>The code computes logarithms for integers larger than the largest representable float. For example, the largest float is on the order of 10<sup>308</sup>, and yet the following code works.</p>
<pre>&gt;&gt;&gt; import math
&gt;&gt;&gt; math.log10(10**400)
400.0
</pre>
<p>The <code>log</code>, <code>log2</code>, and <code>log10</code> functions have some code inside that handles large integers specially. It doesn&#8217;t simply convert the integers to floats before taking the logarithm. If it did, it would overflow. If you replace <code>math</code> with <code>numpy</code> above, the code will fail. NumPy&#8217;s implementation of logarithms is more what I would expect.</p>
<p>While playing around with this I also noticed that you can define floats larger than the largest float without warnings.</p>
<pre>&gt;&gt;&gt; math.log(1e308)
709.1962086421661
&gt;&gt;&gt; math.log(1e309)
inf
</pre>
<p>This isn&#8217;t a feature of <code>math.log</code> but of how Python handles scientific notation. The expression <code>1e308</code> is the floating point representation of 10<sup>308</sup>. It is a float, not an int.</p>
<pre>&gt;&gt;&gt; type(1e308)
&lt;class 'float'&gt;
</pre>
<p>The expression <code>1e309</code> is also a float. But since it&#8217;s larger than is possible for a float, Python interprets it as <code>inf</code>. The code</p>
<pre>math.log(1e309)</pre>
<p>returns <code>inf</code> based on the reasoning that log(∞) = ∞.</p>
<p>That explains the following behavior:</p>
<pre>&gt;&gt;&gt; 1e309 == 1e310
True
</pre>
<p>The expressions <code>1e309</code> and <code>1e310</code> are equal because both are alternate ways of writing <code>inf</code>.</p>The post <a href="https://www.johndcook.com/blog/2026/08/05/math-log/">The code that didn’t break</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/05/math-log/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Enumerating trees and circles</title>
		<link>https://www.johndcook.com/blog/2026/08/05/enumerating-trees-and-circles/</link>
					<comments>https://www.johndcook.com/blog/2026/08/05/enumerating-trees-and-circles/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 05 Aug 2026 14:49:35 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247540</guid>

					<description><![CDATA[<p>A few days ago I wrote a post on counting rooted trees. That post looked at the sequence c(n) which counts the number of rooted trees with n nodes. Here one node is distinguished as the root, but the nodes below the root are not distinguished from each other; all that matters is how the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/05/enumerating-trees-and-circles/">Enumerating trees and circles</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>A few days ago I wrote a post on <a href="https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/">counting rooted trees</a>. That post looked at the sequence <em>c</em>(<em>n</em>) which counts the number of rooted trees with <em>n</em> nodes. Here one node is distinguished as the root, but the nodes below the root are not distinguished from each other; all that matters is how the nodes are connected.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" style="background-color: white;" src="https://www.johndcook.com/rooted_trees_order_1_to_4.svg" width="680" height="600" /></p>
<p>The number of rooted trees with <em>n</em> nodes is the same as the number of ways to configure <em>n</em> − 1 non-overlapping circles. Not only are the counts the same, there is a natural correspondence between the trees and the circles. It&#8217;s not obvious that there should be such a correspondence, with the right notation the correspondence is sort of a pun.</p>
<p>The standard way to represent unlabeled trees is as a <a href="https://www.johndcook.com/blog/2022/10/26/multisets/">multiset</a> of their children. We use a multiset, not a set, because some elements will be repeated. We represent a leaf as a pair of parentheses: <code>()</code>.</p>
<p>There is only one rooted tree with one node: <code>()</code>.</p>
<p>There is only one rooted tree with one two nodes: <code>(())</code>. Here the outer parentheses represent the root node and the inner parentheses represent its child.</p>
<p>There are two rooted trees with three nodes, and we can represent them as <code>((()))</code> and <code>((),())</code>. The first is the straight line tree: a node that has a single child node that has a single child node. The second is a node that branches to two nodes. (Here&#8217;s where we need multisets.)</p>
<p>The four rooted trees with four nodes can be represented as <code>(((())))</code>, <code>((((),()))</code>, <code>((),(()))</code>, and <code>((),(),(),())</code>.</p>
<p>Here are the nine rooted trees with five nodes:</p>
<pre>((((()))))
((((),())))
(((),(())))
(((),(),()))
((()),(()))
((),((())))
((),((),()))
((),(),(()))
((),(),(),())
</pre>
<p>The correspondence with non-overlapping circles removes the outer parentheses then joins the rest to form circles, with nested parentheses corresponding to concentric circles. A more geometric way to see the correspondence is to start at the bottom of the tree, replace leaves with circles, then work your way up circling connected components.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/nonoverlapping_circles2.png" width="362" height="896" /></p>The post <a href="https://www.johndcook.com/blog/2026/08/05/enumerating-trees-and-circles/">Enumerating trees and circles</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/05/enumerating-trees-and-circles/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Mathematical alchemy</title>
		<link>https://www.johndcook.com/blog/2026/08/04/metallic-alchemy/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 04 Aug 2026 13:18:58 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247536</guid>

					<description><![CDATA[<p>After writing the previous post about metallic ratios, I thought about the analogy to alchemy and the attempt to make precious metals out of base metals. When can you make one metallic ratio out of another? Can you make the golden ratio out of the lead ratio? Before we can make gold out of lead, [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/04/metallic-alchemy/">Mathematical alchemy</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>After writing the <a href="https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/">previous post</a> about metallic ratios, I thought about the analogy to alchemy and the attempt to make precious metals out of base metals.</p>
<p>When can you make one metallic ratio out of another? Can you make the golden ratio out of the lead ratio?</p>
<p>Before we can make gold out of lead, we have to say what lead is.</p>
<h2>Defining metallic ratios</h2>
<p>The metallic ratios <em>M</em>(<em>n</em>) can be defined several ways. The most interesting definition is the number whose continued fraction representation contains all <em>n</em>s. A more prosaic but more convenient definition is the larger number that equals its reciprocal plus <em>n</em>, which can be found using the quadratic formula.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/metallic_ratio_def.svg" alt="M(n) = n + \cfrac{1}{n+\cfrac{1}{n+\cfrac{1}{n+\cdots}}} = \frac{n + \sqrt{n^2 + 4}}{2}" width="344" height="110" /></p>
<p>The golden ratio is <em>M</em>(1), the silver ratio is <em>M</em>(2), and the bronze ratio is <em>M</em>(3).</p>
<h2>Gold from silver and bronze?</h2>
<p>Can you make the golden ratio out of the silver and bronze ratios? Not by integer arithmetic. The golden ratio involves √5, the silver ratio √2 and the bronze ratio √13. No integer operations on the latter two radicals will produce the former, though you can come arbitrarily close.</p>
<h2>Gold from lead</h2>
<p>The metallic ratios for <em>n</em> &gt; 3 don&#8217;t have standard names, but let&#8217;s call <em>M</em>(4) the lead ratio. Can you make the golden ratio out of the lead ratio? Yes you can:</p>
<p style="padding-left: 40px;"><em>M</em>(1) = (<em>M</em>(4) − 1)/2.</p>
<h2>General solution</h2>
<p>In general, when can you make <em>M</em>(<em>n</em>) out of <em>M</em>(<em>m</em>)? In abstract terms the question is when the fields</p>
<p style="padding-left: 40px;">ℚ(√(<em>n</em>² + 4))</p>
<p>and</p>
<p style="padding-left: 40px;">ℚ(√(<em>m</em>² + 4))</p>
<p>are the same, i.e. when adjoining √(<em>n</em>² + 4) to the rational numbers gives the same field as adjoining √(<em>m</em>² + 4) to the rational numbers. This occurs if and only if</p>
<p style="padding-left: 40px;">(<em>n</em>² + 4)/(<em>m²</em> + 4)</p>
<p>is the square of a rational number.</p>
<h2>Bronze from copper and tin</h2>
<p>Can you make bronze out of copper and tin? Yes, if you define <em>M</em>(36) to be the copper ratio and <em>M</em>(393) to be the tin ratio, because</p>
<p style="padding-left: 40px;">(3² + 4)/(36² + 4) = (1/10)²</p>
<p>and</p>
<p style="padding-left: 40px;">(3² + 4)/(292² + 4) = (1/109)².</p>The post <a href="https://www.johndcook.com/blog/2026/08/04/metallic-alchemy/">Mathematical alchemy</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Ratio of metallic ratios</title>
		<link>https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/</link>
					<comments>https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 04 Aug 2026 11:41:49 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247532</guid>

					<description><![CDATA[<p>The golden ratio is the first and best known of the metallic ratios. I&#8217;ve written about the silver ratio a few times, most recently here. And I&#8217;ve mentioned the bronze ratio a couple times. The metallic ratios after bronze don&#8217;t have standard names. The nth metallic ratio M(n) is the number whose continued fraction representation [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/">Ratio of metallic ratios</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The golden ratio is the first and best known of the metallic ratios. I&#8217;ve written about the silver ratio a few times, most recently <a href="https://www.johndcook.com/blog/2026/06/30/silver-kings/">here</a>. And I&#8217;ve mentioned the <a href="https://www.johndcook.com/blog/2023/04/14/metallic-ratios/">bronze ratio</a> a couple times. The metallic ratios after bronze don&#8217;t have standard names.</p>
<p>The <em>n</em>th metallic ratio <em>M</em>(<em>n</em>) is the number whose continued fraction representation contains all <em>n</em>s.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/metallic_ratio.svg" alt="n + \cfrac{1}{n+\cfrac{1}{n+\cfrac{1}{n+\cdots}}} = \frac{n + \sqrt{n^2 + 4}}{2}" width="276" height="95" /></p>
<p>When <em>n</em> = 1, 2, and 3 we get the gold, silver, and bronze ratios.</p>
<p>You can approximate any positive real number as a ratio of metallic ratios. To see this, note that for large <em>n</em>, <i>M</i>(<em>n</em>) is approximately <em>n</em>. For any positive rational number <em>a</em>/<em>b</em>,</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/metallic_ratio_ratio.svg" alt="\lim_{n\to\infty} \frac{M(na)}{M(nb)} = \frac{a}{b}" width="131" height="45" /></p>
<p>and so you can make <em>M</em>(<em>na</em>) / <em>M</em>(<em>nb</em>) as close to <em>a</em>/<em>b</em> as you like by taking <em>n</em> large enough. And since the rationals are dense in the reals, you can approximate any positive real number as close as you&#8217;d like.</p>
<p>Let&#8217;s look for metallic ratios whose ratios approximate π to within 0.001 with the following Python code.</p>
<pre>from math import pi, sqrt

M = lambda n: 0.5*(n + sqrt(n**2 + 4))

for n in range(1, 100):
    a = round(pi*n)
    b = n
    r = M(a)/M(b)
    if abs(r - pi) &lt; 0.001:
        print(a, b, r)
</pre>
<p>This shows</p>
<p style="padding-left: 40px;">π ≈ <em>M</em>(132) / <em>M</em>(42) = 3.1412…</p>
<p>Could we find smaller numbers that work? The following code shows the answer is no.</p>
<pre>k = 132 + 42
# loop over numbers whose sum is less than k
for n in range(1, k):
    for a in range(1, n):
        b = n - a
        r = M(a)/M(b)
        if abs(r - pi) &lt; 0.001:
            print(a, b, r)
            exit()
</pre>
<h2>Related posts</h2>
<ul>
<li class='link'><a href='https://www.johndcook.com/blog/2024/09/01/pell-numbers/'>Pell is to silver as Fibonacci is to gold</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2024/10/10/golden-ellipse/'>Golden ellipse</a></li>
<li class='link'><a href='https://www.johndcook.com/blog/2026/06/29/derivative-equals-inverse/'>Derivative equals inverse</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/">Ratio of metallic ratios</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/04/ratio-of-metallic-ratios/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Holonomic functions</title>
		<link>https://www.johndcook.com/blog/2026/08/02/holonomic-functions/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 02 Aug 2026 20:47:53 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<category><![CDATA[Special functions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247524</guid>

					<description><![CDATA[<p>Yesterday I wrote that a lot of the special functions that pop up in mathematical physics are solutions to second order linear differential equations with polynomial coefficients. More generally, holonomic functions are defined to be those functions that are the solutions to linear differential equations, of any order, with polynomial coefficients. Most special functions are [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/02/holonomic-functions/">Holonomic functions</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p><a href="https://www.johndcook.com/blog/2026/08/01/why-polynomial-coefficients/">Yesterday</a> I wrote that a lot of the special functions that pop up in mathematical physics are solutions to second order linear differential equations with polynomial coefficients. More generally,<strong> holonomic functions</strong> are defined to be those functions that are the solutions to linear differential equations, of any order, with polynomial coefficients.</p>
<p>Most special functions are holonomic. To quantify that statement, I went through the special functions covered in Abramowitz and Stegun. The large majority are holonomic, though some common functions like the gamma function are not holonomic.</p>
<p><a href="https://www.johndcook.com/holonomic_odes_abramowitz_stegun.pdf">This report</a> goes through the functions in A&amp;S. For those that are holonomic, it gives the differential equation that the function solves. The large majority of these equations are second order, but not all. And the coefficients are nearly always first or second order polynomials, rarely higher order.</p>The post <a href="https://www.johndcook.com/blog/2026/08/02/holonomic-functions/">Holonomic functions</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Estimating a cumulative sum</title>
		<link>https://www.johndcook.com/blog/2026/08/02/estimating-a-cumulative-sum/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 02 Aug 2026 17:57:31 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247518</guid>

					<description><![CDATA[<p>In this post I mentioned two series which I denoted t(n) and c(n). The former is the number of unlabeled rooted trees with n nodes. The latter is the cumulative sum of the former, i.e. The sequence c(n) is also the number of constraints on an n-step Runge-Kutta method; that&#8217;s how I became interested in it. Now the t(n) sequence [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/02/estimating-a-cumulative-sum/">Estimating a cumulative sum</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>In <a href="https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/">this post</a> I mentioned two series which I denoted <em>t</em>(<em>n</em>) and <em>c</em>(<em>n</em>). The former is the number of unlabeled rooted trees with <em>n</em> nodes. The latter is the cumulative sum of the former, i.e.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/cumsum_asymp1.svg" alt="c(n) = t(1) + t(2) + t(3) + \cdots + t(n)" width="320" height="18" /></p>
<p>The sequence <em>c</em>(<em>n</em>) is also the number of constraints on an <em>n</em>-step Runge-Kutta method; that&#8217;s how I became interested in it.</p>
<p>Now the <em>t</em>(<em>n</em>) sequence has been cataloged as OEIS <a href="https://oeis.org/A000081">A000081</a> and OEIS gives the asymptotic estimate of <em>t</em>(<em>n</em>) for large <em>n</em> as</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/cumsum_asymp2.svg" alt="t(n) \sim C \frac{a^n}{n^{3/2}}" width="105" height="42" /></p>
<p>where <em>C</em> = 0.4399… and α = 2.9557….</p>
<p>The cumulative sum of <em>t</em>(<em>n</em>), what I&#8217;ve called <em>c</em>(<em>n</em>), is also cataloged in OEIS, sequence number <a href="https://oeis.org/A087803">A087803</a>. However, OEIS does not give an asymptotic estimate for this sequence. I&#8217;ll give one here.</p>
<p>(Update: After looking closer at the page for A087803 I see that there is an asymptotic formula, the same one derived here.)</p>
<p>The basis for my derivation is to assume the cumulative sum of the asymptotic estimates gives an asymptotic estimate of the cumulative sum. This is justified by the fact that the sequence is increasing rapidly and only the last few terms contribute much relatively to the sum.</p>
<p>The technique illustrated here would be applicable to the cumulative sum of other series whose asymptotic form is known.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/cumsum_asymp.svg" alt="\begin{align*} c(n) &amp;= \sum_{n=1}^N t(n) \\ &amp;\sim \sum_{n=1}^N C \frac{a^n}{n^{3/2}}\\ &amp;= C \frac{a^N}{N^{3/2}} \sum_{k=0}^{N-1} a^{-k}\left(1 - \frac{k}{N} \right)^{-3/2} \\ &amp;\sim C \frac{a^N}{N^{3/2}} \sum_{k=0}^\infty a^{-k} \\ &amp;= C \frac{a^N}{N^{3/2}} \frac{a}{a-1} \\ &amp;= C \frac{a^{N+1}}{(a-1)N^{3/2}} \end{align*} " width="280" height="371" /></p>
<p>Here&#8217;s code to visualize the rate of convergence.</p>
<pre>import numpy as np
import matplotlib.pyplot as plt

# from https://oeis.org/A000081/b000081.txt
A000081 = [
    0,
    1,
    1,
    2,
    4,
    ...
    51384328351659326880337136395054298255277970,
]  
A087803 = np.cumsum(A000081)

def approx(n):
    C = 0.43992401257102530
    a = 2.95576528565199497
    return C*a**(n+1)*n**(-3/2)/(a - 1)

n = np.arange(len(A087803))
ratio = A087803/approx(n)

plt.plot(n[1:], ratio[1:])
plt.plot(n, 0*n + 1, '--')
plt.xlabel("$n$")
plt.ylabel("exact/approx")
plt.show()
</pre>
<p>Here&#8217;s the plot:</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/cumsum_asymp_plot.png" width="640" height="480" /></p>The post <a href="https://www.johndcook.com/blog/2026/08/02/estimating-a-cumulative-sum/">Estimating a cumulative sum</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Why polynomial coefficients?</title>
		<link>https://www.johndcook.com/blog/2026/08/01/why-polynomial-coefficients/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 01 Aug 2026 20:18:41 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<category><![CDATA[Science]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247512</guid>

					<description><![CDATA[<p>Second order linear differential equations with polynomial coefficients form their own area of study. This seems like a narrow class of equations, but it&#8217;s very important in applications. This class of equations seems like a mathematically natural topic, but why is it so important in applications? I did a PhD in differential equations without ever [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/01/why-polynomial-coefficients/">Why polynomial coefficients?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Second order linear differential equations with polynomial coefficients form their own area of study. This seems like a narrow class of equations, but it&#8217;s very important in applications.</p>
<p>This class of equations seems like a mathematically natural topic, but why is it so important in applications? I did a PhD in differential equations without ever learning why. The theory of second order linear equations with polynomial coefficients is too complicated for undergraduate courses [0] and too well-established for graduate courses [1]. </p>
<p>The explanation that I was missing can be found in the first chapter of [2]. The PDEs that are common in physics are separable in various coordinate systems, meaning that in these coordinate systems the PDEs reduce to ODEs. These ODEs either have polynomial coefficients, or there is a change of variables which makes the ODEs have polynomial coefficients.</p>
<p>See this <a href="https://www.johndcook.com/separable_helmholtz.pdf">writeup</a> that looks at the Helmholtz and Laplace equations in 11 coordinate systems.</p>
<p>[0] You may see the simplest parts of the theory in a section on solving ODEs with power series. But textbooks don&#8217;t go very far for good reasons.</p>
<p>[1] Unfortunately, a lot of really useful topics are left out of the graduate curriculum because they&#8217;re too well understood to provide thesis topics. Or the problems that are still open have been open for so long that they&#8217;re likely too hard to be cracked by a graduate student.</p>
<p>[2] Gerhard Kristensson. Second Order Differential Equations: Special Functions and their Classification. Springer, 2010.</p>The post <a href="https://www.johndcook.com/blog/2026/08/01/why-polynomial-coefficients/">Why polynomial coefficients?</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Counting rooted trees</title>
		<link>https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 01 Aug 2026 16:15:53 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<category><![CDATA[Differential equations]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247502</guid>

					<description><![CDATA[<p>Combinatorial problems can be interesting for their own sake, but they are more interesting when there is a connection to a problem outside combinatorics, and the more unexpected the connection the better. Counting the number of unlabeled rooted trees [1] with n nodes is a pure mathematics problem. Designing numerical methods for solving differential equations [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/">Counting rooted trees</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Combinatorial problems can be interesting for their own sake, but they are more interesting when there is a connection to a problem outside combinatorics, and the more unexpected the connection the better.</p>
<p>Counting the number of unlabeled rooted trees [1] with <em>n</em> nodes is a pure mathematics problem. Designing numerical methods for solving differential equations is an applied mathematics problem. And yet the two are closely linked.</p>
<p>Let <em>t</em>(<em>n</em>) be the number of distinct unlabeled rooted trees with <em>n</em> nodes. The diagram below shows that the first few terms of this sequence are 1, 1, 2, and 4.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" style="background-color: white;" src="https://www.johndcook.com/rooted_trees_order_1_to_4.svg" width="680" height="600" /></p>
<h2>Recursive calculation</h2>
<p>The values of <em>t</em>(<em>n</em>) can be computed recursively using</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/counting_trees.svg" alt="\begin{align*} g_k &amp;= \sum_{d\mid k} d t_d \\ t_1 &amp;= 1 \\ t_n &amp;= \frac{1}{n-1} \sum_{k=1}^{n-1} g_k t_{n-k} \text{ for } n &gt; 1&lt;br /&gt;
\end{align*}&lt;br /&gt;
" width="247" height="147" /></p>
<p>You can implement this in Python as follows.</p>
<pre>from sympy import divisors

def t(n):
    if n &lt;= 1:
        return 1 if n == 1 else 0
    return sum(g(k) * t(n - k) for k in range(1, n)) // (n - 1)

def g(k):
    return sum(d * t(d) for d in divisors(k))
</pre>
<p>This code is correct, but it will run more efficiently if you cache function values to avoid calculating the same values over and over. You can do this by adding</p>
<pre>from functools import lru_cache</pre>
<p>and writing <code>@lru_cache(maxsize=None)</code> above both function definitions.</p>
<h2>Connection to Runge-Kutta</h2>
<p>In an <a href="https://www.johndcook.com/blog/2026/07/31/runge-kutta-design/">earlier post</a> I showed that designing a 4-stage explicit Runge-Kutta method required solving a system of 8 equations in 10 unknowns, leaving two degrees of freedom in the solutions.</p>
<p>The number of constraints <em>c</em>(<em>s</em>) needed to design an <em>s</em>-stage explicit RK method is equal to the number of rooted trees with up to <em>s</em> nodes:</p>
<p style="padding-left: 40px;"><em>c</em>(<em>s</em>) = <em>t</em>(1) + <em>t</em>(2) + <em>t</em>(3) + … + <em>t</em>(<em>s</em>)</p>
<p>This is because there is a one-to-one correspondence between constraints on the <em>n</em>th derivative of an RK formula and rooted trees, and an <em>s</em> stage method has to satisfy the constraints of all stages up to <em>s</em>. In the example of the 4th order RK method, we have</p>
<p style="padding-left: 40px;"><em>c</em>(4) = <em>t</em>(1) + <em>t</em>(2)  + <em>t</em>(3) + <em>t</em>(4) = 1 + 1 + 2 + 4 = 8.</p>
<p>The first few values [2] of <em>t</em>(<em>n</em>) are</p>
<p style="padding-left: 40px;">1, 1, 2, 4, 9, 20, 48, 115, 286, 719, 1842, 4766, 12486, 32973, …</p>
<p>and so you can see that <em>t</em>(<em>n</em>) grows quickly. In fact, it grows exponentially [3].</p>
<p>However, the number of parameters in an <em>s</em> stage RK method is <em>s</em>(<em>s</em> + 1)/2. The number of equations grows exponentially and the number of variables grows only quadratically, so at some point you have more equations than variables. That&#8217;s already the case for <em>s</em> = 5 because you have 17 constraints on 15 variables. The system has a solution because symmetry considerations render some of the equations redundant.</p>
<p>A 10th order RK method requires 17 stages. (See the <a href="https://www.johndcook.com/blog/2026/08/01/butcher-barrier/">previous post</a> for why the number of stages exceeds the order when the order is greater than 4.) Designing such a method would require solving over a million equations in 153 variables, and yet it can be done. [4]</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2026/07/31/runge-kutta-design/">Solving the RK4 equations</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/07/27/counting-permutations-with-roots/">Counting permutations with roots</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2026/06/30/dna-sequence-alignment-and-kings/">DNA alignment and Kings</a></li>
</ul>
<p>[1] This is a slightly contradictory term. Unlabeled means the we don&#8217;t distinguish the nodes. But we do distinguish one node, namely the root.</p>
<p>[2] See OEIS <a href="https://oeis.org/A000081">A000081</a>.</p>
<p>[2] Richard Otter proved in 1948 that the number of unlabeled rooted trees with <em>n</em> nodes is asymptotically <em>C</em> α<sup><em>n</em></sup> / <em>n</em><sup>−3/2</sup> where <em>C</em> = 0.4399… and α = 2.9557…. The cumulative sum is at least this large since Otter&#8217;s estimate gives the size of the last term in the sum.</p>
<p>[3] E. Hairer. A Runge-Kutta Method of Order 10. J. Inst. Maths Applics (1978) 21, 47-59</p>The post <a href="https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/">Counting rooted trees</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Runge-Kutta order versus stages</title>
		<link>https://www.johndcook.com/blog/2026/08/01/butcher-barrier/</link>
					<comments>https://www.johndcook.com/blog/2026/08/01/butcher-barrier/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 01 Aug 2026 15:58:15 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247499</guid>

					<description><![CDATA[<p>The textbook version of the Runge-Kutta method for solving differential equations has 4 stages and has 4th order error. For lower order versions of RK the number of stages s also matches the order of the error p. But in order to achieve error on the order of p ≥ 5, you need more than p [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/08/01/butcher-barrier/">Runge-Kutta order versus stages</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The textbook version of the Runge-Kutta method for solving differential equations has 4 stages and has 4th order error. For lower order versions of RK the number of stages <em>s</em> also matches the order of the error <em>p</em>. But in order to achieve error on the order of <em>p</em> ≥ 5, you need more than <em>p</em> stages. This is known as the Butcher barrier.</p>
<p>Before going any further, let&#8217;s back up and say what we mean by stages and by order.</p>
<h2>Stages</h2>
<p>The number of stages in an RK method to solve the equation</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/first_order_ode.svg" alt="y' = f(t, y)" width="82" /></p>
<p>is the number of evaluations of the function <em>f</em> on the right-hand side. For example, the textbook RK4 method estimates the solution at each step by</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/rk4.svg" alt="y_{n+1} = y_n + \frac{h}{6}\left( k_{n1} + 2k_{n2} + 2k_{n3} + k_{n4}\right)" width="305" /></p>
<p>where</p>
<p><img decoding="async" class="aligncenter" src="https://www.johndcook.com/rk4k.svg" alt="k_{n1} &amp;=&amp; f(t_n, y_n) \\ k_{n2} &amp;=&amp; f(t_n + 0.5h, y_n + 0.5hk_{n1}) \\ k_{n3} &amp;=&amp; f(t_n + 0.5h, y_n + 0.5hk_{n2}) \\ k_{n4} &amp;=&amp; f(t_n + h, y_n + hk_{n3}) \\" width="280" /></p>
<p>which requires four stages, i.e. four evaluations of <em>f</em>.</p>
<h2>Order</h2>
<p>A differential equation solver is said to have order <em>p</em> if the local error, the error after one step of size <em>h</em>, is <em>O</em>(<em>h</em><sup><em>p</em> + 1</sup>). Then after solving an ODE over a period of time <em>T</em> with <em>N</em> = <em>T</em>/<em>h</em> steps, the global error is <em>O</em>(<em>h</em><sup><em>p</em></sup>). So, for example, if <em>p</em> = 4, you would expect that cutting your step size <em>h</em> in half would cut your error at <em>T</em> by a factor of 16.</p>
<h2>More stages than the order</h2>
<p>John C. Butcher proved that an explicit RK method of order <em>p</em> requires <em>s</em> stages where <em>s</em> &gt; <em>p</em> if <em>p</em> &gt; 4.</p>
<p>An important example is the Dormand-Prince method. It is a version of RK that has order 5 and 7 stages. The clever thing about this method is that you can make a 4th order solver out of a subset of its function evaluations.</p>
<p>That means that after you&#8217;ve evaluated one step of the 5th order method, you can also evaluate a 4th order method essentially for free. And by comparing them, you can get a sense of the error. If the solutions given by the two methods are substantially different, you have probably taken too big a step and need to back up. If the two solutions essentially agree, you&#8217;re probably good to take the next step.</p>
<p>For an explict RK method to have order 5, 6, or 7 you need at least 6, 7, or 9 stages respectively.</p>The post <a href="https://www.johndcook.com/blog/2026/08/01/butcher-barrier/">Runge-Kutta order versus stages</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/08/01/butcher-barrier/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Solving the RK4 design equations</title>
		<link>https://www.johndcook.com/blog/2026/07/31/runge-kutta-design/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Fri, 31 Jul 2026 18:59:02 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<category><![CDATA[Mathematica]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247492</guid>

					<description><![CDATA[<p>I was digging into the Runge-Kutta method for solving differential equations and a line from [1] piqued my curiosity. These calculations, which are not reproduced in Kutta&#8217;s paper (they are however in Huen (1900)), are very tedious. The calculations are a set of eight constraints that the parameters of a fourth-order Runge-Kutta method must satisfy. [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/31/runge-kutta-design/">Solving the RK4 design equations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I was digging into the Runge-Kutta method for solving differential equations and a line from [1] piqued my curiosity.</p>
<p style="padding-left: 40px;">These calculations, which are not reproduced in Kutta&#8217;s paper (they are however in Huen (1900)), are very tedious.</p>
<p>The calculations are a set of eight constraints that the parameters of a fourth-order Runge-Kutta method must satisfy. I wondered how well Mathematica might have done at assisting Mr. Huen in his &#8220;very tedious&#8221; calculations if it had been available in 1900.</p>
<p>I go into Runge-Kutta methods in <a href="https://www.johndcook.com/blog/2020/02/13/runge-kutta-methods/">this post</a>. Here I&#8217;d like to concentrate on a step in the design of the methods, namely solving the set of equations alluded in the quote above.</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/RK4.svg" alt="\begin{align*} b_1 + b_2 + b_3 + b_4 &amp;= 1 \\ b_2 c_2 + b_3 c_3 + b_4 c_4 &amp;= \frac{1}{2} \\ b_2 c_2^2 + b_3 c_3^2 + b_4 c_4^2 &amp;= \frac{1}{3} \\ b_3 a_{32} c_2 + b_4(a_{42} c_2 + a_{43} c_3) &amp;= \frac{1}{6} \\ b_2 c_2^3 + b_3 c_3^3 + b_4 c_4^3 &amp;= \frac{1}{4} \\ b_3 c_3 a_{32} c_2 + b_4 c_4(a_{42} c_2 + a_{43} c_3) &amp;= \frac{1}{8} \\ b_3 a_{32} c_2^2 + b_4(a_{42} c_2^2 + a_{43} c_3^2) &amp;= \frac{1}{12} \\ b_4 a_{43} a_{32} c_2 &amp;= \frac{1}{24} \end{align*} " width="297" height="353" /></p>
<p>The first thing to note is that there are 10 variables and only 8 equations, and so the solution is not fully determined. What we think of as <em>the</em> fourth order Runge-Kutta method is in fact <em>a</em> fourth order Runge-Kutta method.</p>
<p>One could argue that we should have <em>b</em><sub>2</sub> = <em>b</em><sub>3</sub> and <em>c</em><sub>2</sub> = <em>c</em><sub>3</sub>. With these additional equations, the system of equations has a unique solution, and Mathematic finds it easily.</p>
<pre>eqs = {
    b1 + b2 + b3 + b4 == 1,
    b2*c2 + b3*c3 + b4*c4 == 1/2,
    b2*c2^2 + b3*c3^2 + b4*c4^2 == 1/3,
    b3*a32*c2 + b4*(a42*c2 + a43*c3) == 1/6,
    b2*c2^3 + b3*c3^3 + b4*c4^3 == 1/4,
    b3*c3*a32*c2 + b4*c4*(a42*c2 + a43*c3) == 1/8,
    b3*a32*c2^2 + b4*(a42*c2^2 + a43*c3^2) == 1/12,
    b4*a43*a32*c2 == 1/24,
    b2 == b3,
    c2 == c3
};

vars = {b1, b2, b3, b4, c2, c3, c4, a32, a42, a43};

solution = Solve[eqs, vars]
</pre>
<p>This returns the parameters used for the version of Runge-Kutta presented in every textbook.</p>
<p>If you keep the requirement <em>b</em><sub>2</sub> = <em>b</em><sub>3</sub> but substitute the requirement 2<em>c</em><sub>2</sub> = <em>c</em><sub>3</sub> for <em>c</em>&#8216;s Mathematica will return the coefficients for the so-called Runge-Kutta 3/8 rule. This method has some slight advantages by some criteria.</p>
<p>In 1951 Gill [2] discovered a fourth order Runge-Kutta rule optimized for running in extremely constrained computer hardware. It&#8217;s a strange method, with irrational parameters, but one that was a very clever response to the limitations of its time.</p>
<p><strong>Update</strong>: See <a href="https://www.johndcook.com/blog/2026/08/01/counting-rooted-trees/">this post</a> for a discussion of the parameters and constriants for higher-ordered RK methods.</p>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/blog/2020/02/19/dormand-prince/">Dormand and Prince</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2016/06/02/ode-solver-as-a-functional-fold/">RK4 as a fold</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2020/02/02/stiff-differential-equations/">Stiff differential equations</a></li>
</ul>
<p>[1] Hairer, Nørsett, and Wanner. Solving Ordinary Differential Equations I: Nonstiff Problems. Springer-Verlag 1987.</p>
<p>[2] A. Gill. A process for the step-by-step integration of differential equations in an automatic digital computing machine. Proc. Cambridge Philos. Soc., vol 27, pp 95–108.</p>The post <a href="https://www.johndcook.com/blog/2026/07/31/runge-kutta-design/">Solving the RK4 design equations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Inverse factorial improved</title>
		<link>https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/</link>
					<comments>https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 28 Jul 2026 14:11:30 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Special functions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247470</guid>

					<description><![CDATA[<p>A couple years ago I wrote about how to compute the inverse of factorial. I used that code in writing the previous post because the post required solving the equation ⌊log2(n!)⌋ ≥ b given b. That is, given a number of bits b, find the smallest value of n such that n! ≥ 2b. What the [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/">Inverse factorial improved</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p><a href="https://www.johndcook.com/blog/2024/01/01/inverse-factorial/">A couple years ago</a> I wrote about how to compute the inverse of factorial. I used that code in writing the previous post because the post required solving the equation</p>
<p style="padding-left: 40px;">⌊log<sub>2</sub>(<em>n</em>!)⌋ ≥ <em>b</em></p>
<p>given <em>b</em>. That is, given a number of bits <em>b</em>, find the smallest value of <em>n</em> such that <em>n</em>! ≥ 2<sup><em>b</em></sup>.</p>
<h2>What the code got right</h2>
<p>Looking back on the code in that post, there are a few changes I&#8217;d like to make. But first of all, I&#8217;d like to point out something the post does right: instead of trying to solve</p>
<p style="padding-left: 40px;">Γ(<em>y</em>) = <em>x</em></p>
<p>it solves</p>
<p style="padding-left: 40px;">log Γ(<em>y</em>) = log <em>x</em>.</p>
<p>That&#8217;s why the argument to <code>inverse_log_gamma</code> is <code>logarg</code>. That makes the code useful for values of <em>x</em> that would far exceed the maximum floating point value, such as in the calculations for the previous post.</p>
<h2>What I&#8217;d change</h2>
<h3>Rounding</h3>
<p>The function <code>inverse_factorial</code> from the old post solves finds the closest integer solution. It would be better for it to return the solution without rounding and then let the user round result if they want to. In my calculations in the previous post, I wanted to take the floor, not round.</p>
<h3>Newton&#8217;s method</h3>
<p>The code in the previous post uses the bisection method. This method is very safe, and fast enough for my purposes, but it could be made faster. Newton&#8217;s method is faster, but it can be ill-behaved if you don&#8217;t start close enough to the solution.</p>
<p>It&#8217;s safe to use Newton&#8217;s method to invert log Γ for two reasons. First, you can get a good starting point based on Stirling&#8217;s approximation. Second, and more importantly, log Γ is convex. Newton&#8217;s method will converge from <em>any</em> starting point when applied to a convex function. A little caution is necessary because log Γ is not convex everywhere, but it is convex on the positive real axis.</p>
<p>Another difficulty with Newton&#8217;s method is that you need to supply the derivative of the function whose root you&#8217;re trying to find. But this isn&#8217;t an issue here because the derivative of log Γ is the digamma function, which is implemented in SciPy.</p>
<h3>Tolerance</h3>
<p>Finally, the previous code used the default tolerance for deciding when to stop refining the solution. The revised method lets the user specify tolerance. It provides a default value, but that default is visible in the function call, not hidden down in SciPy.</p>
<h2>Revised code</h2>
<p>Here&#8217;s the revised code.</p>
<pre>from scipy.special import gammaln, digamma
from scipy.optimize import newton

def inverse_log_gamma(logarg, tol=1e-12):
    assert(logarg &gt; 0)    
    x0 = logarg / log(logarg + 1) + 1 if logarg &gt; 1 else 2.0
    def f(z): return gammaln(z) - logarg
    return newton(f, x0, fprime=digamma, tol=tol)

def inverse_factorial(logarg):
    g = inverse_log_gamma(logarg)
    return g - 1 
</pre>The post <a href="https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/">Inverse factorial improved</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Cryptographic Keys and Decks of Cards</title>
		<link>https://www.johndcook.com/blog/2026/07/28/keys-and-cards/</link>
					<comments>https://www.johndcook.com/blog/2026/07/28/keys-and-cards/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Tue, 28 Jul 2026 12:13:17 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<category><![CDATA[Cryptography]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247468</guid>

					<description><![CDATA[<p>The previous post looked at the idea of storing a cryptographic key in the order of a deck of cards. A deck of 52 cards can store 225 bits of data because ⌊log2(52!)⌋ = 225. Here ⌊x⌋ is x rounded down to the nearest integer. If we want to store bigger keys, we&#8217;re going to need [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/28/keys-and-cards/">Cryptographic Keys and Decks of Cards</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://www.johndcook.com/blog/2026/07/27/hiding-data-in-permutations/">previous post</a> looked at the idea of storing a cryptographic key in the order of a deck of cards. A deck of 52 cards can store 225 bits of data because</p>
<p style="padding-left: 40px;">⌊log<sub>2</sub>(52!)⌋ = 225.</p>
<p>Here ⌊<em>x</em>⌋ is <em>x</em> rounded down to the nearest integer.</p>
<p>If we want to store bigger keys, we&#8217;re going to need a bigger deck of cards.</p>
<h2>Bitcoin</h2>
<p>A Bitcoin key has 256 bits, which would require a deck of 58 cards. There is a card game called Zwicker that uses a deck of 58 cards, the usual 52 cards plus six jokers. So you could store a Bitcoin key in the permutation of a Zwicker deck.</p>
<p>You could also use a deck of 52 cards, plus 2 jokers, if you also consider orientation. 30 cards are rotationally symmetric, 22 are not, and neither are jokers. So, including two asymmetric jokers, you could add 24 additional bits. Permutations of a 54 card deck can encode 237 bits, and with 24 orientation bits, this is a total of 261 bits.</p>
<h2>RSA</h2>
<p>RSA key sizes vary, but 2048 and 3072 are common. A 2048-bit key would require a deck of 301 cards. Casinos often use a shoe of 312 cards, combining six decks of 52 cards, to deal Baccarat or Blackjack. However, casinos combine identical decks. If you were to combine six unique decks, you could store a 2048-bit key.</p>
<p>Storing a 3072-bit key would require a deck of 422 cards. You could make a deck of 432 cards by combining 8 distinguishable packs of 54 cards (52 + 2 jokers).</p>
<h2>ML-KEM</h2>
<p>ML-KEM is a proposed quantum-resistant replacement for RSA. As with RSA, key sizes for ML-KEM vary, the smallest being ML-<strong>KEM-512</strong> with a key size of 1632 bytes, which equals 13056 bits. This would require a deck of 1442 cards. You could combine 28 distinct packs of 52 cards, but that&#8217;s unwieldy.</p>
<p>This illustrates one of the difficult trade-offs with post-quantum cryptography: key sizes are much bigger. If you wanted to create a deck of 1442 cards, you&#8217;d probably want to make your &#8220;cards&#8221; something other than standard playing cards. You&#8217;d want to use permutations of something else.</p>
<h2>Verification</h2>
<p>The following Python code verifies the calculations above.</p>
<pre>from math import log2, factorial, floor

def capacity(cards):
    return floor(log2(factorial(cards)))

def verify(bits, cards):
    return capacity(cards) &gt;= bits and capacity(cards-1) &lt; bits

print(verify(237, 54))
print(verify(256, 58))
print(verify(2048, 301))
print(verify(3072, 422))
print(verify(1632*8, 1442))
</pre>
<p>For more on how I came up with the deck sizes, see the <a href="https://www.johndcook.com/blog/2026/07/28/inverse-factorial-improved/">next post</a> on computing the inverse factorial.</p>The post <a href="https://www.johndcook.com/blog/2026/07/28/keys-and-cards/">Cryptographic Keys and Decks of Cards</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/28/keys-and-cards/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Hiding data in permutations</title>
		<link>https://www.johndcook.com/blog/2026/07/27/hiding-data-in-permutations/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 27 Jul 2026 23:08:59 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<category><![CDATA[Cryptography]]></category>
		<category><![CDATA[SymPy]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247462</guid>

					<description><![CDATA[<p>The latest issue of Paged Out! has an article by Stephen Hewitt &#8220;An off-line backup of your cryptographic key using playing cards.&#8221; The idea is to use a deck of 52 to store a 128-bit cryptographic key. To erase the key, shuffle the deck. Hewitt gives his algorithm for embedding a key, one that can [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/27/hiding-data-in-permutations/">Hiding data in permutations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://pagedout.institute/download/PagedOut_009.pdf">latest issue</a> of Paged Out! has an article by Stephen Hewitt &#8220;An off-line backup of your cryptographic key using playing cards.&#8221; The idea is to use a deck of 52 to store a 128-bit cryptographic key. To erase the key, shuffle the deck. Hewitt gives his algorithm for embedding a key, one that can be carried out manually but isn&#8217;t maximally efficient.</p>
<p>You could store a 225-bit key as a permutation of 52 cards because</p>
<p style="padding-left: 40px;">log<sub>2</sub>(52!) = 225.581.</p>
<p>But then how would you number permutations so you could go from a number to a particular permutation and later decode the permutation to a number? Is this even practical? For a small number <em>n</em>, you could encode a number <em>k</em> &lt; <em>n</em> by enumerating the first <em>k</em> permutations of a set of <em>n</em> items, and you could decode by enumerating permutations until you find the one you have. But this is completely impractical for large <em>n</em>, such as <em>n</em> = 52.</p>
<p>The process of mapping permutation to an integer is called <b>ranking</b>, and the mapping from an integer to a permutation is called <b>unranking</b>. How efficiently can rankings and unrankings be calculated?</p>
<p>Let <em>n</em> be the number of symbols being permuted. Then there are simple algorithms for ranking and unranking with respect to lexicographical order that have complexity <em>O</em>(<em>n</em>²) and more sophisticated algorithms that have complexity <em>O</em>(<em>n</em> log <em>n</em>). There are also <em>O</em>(<em>n</em>) algorithms that do not preserve lexicographical order.</p>
<p>The <code>Permutations</code> class in SymPy has methods <code>unrank_lex</code> and <code>rank</code> to unrank and rank permutations according to lexicographical order.</p>
<p>The notation the <code>Permutations</code> class uses requires a little explanation. For example, suppose we unrank 2026.</p>
<pre>&gt;&gt;&gt; from sympy.combinatorics import Permutation
&gt;&gt;&gt; Permutation.unrank_lex(52, 2026)
Permutation(45, 47, 51, 48, 46, 50)
</pre>
<p>The output is not a full list of 52 numbers in permuted order; it is only a cycle. The notation refers to the permutation that sends 45 to 47, 47 to 51, …, 50 to 45 and leaves everything else fixed.</p>
<p>If we rank the permutation given above, we get 2026 back.</p>
<pre>&gt;&gt;&gt; Permutation.rank(Permutation(45, 47, 51, 48, 46, 50))
2026
</pre>
<p>Note that we didn&#8217;t say how many elements (45, 47, 51, 48, 46, 50) is a permutation of. Because of lexicographical order, the rank would be the same whether we viewed this as a permutation of 52 objects or of more objects.</p>
<p>Now let&#8217;s do something larger. Let&#8217;s generate a 220-bit number and encode it as a permutation.</p>
<pre>&gt;&gt;&gt; n = random.getrandbits(225)
&gt;&gt;&gt; a = Permutation.unrank_lex(52, n)
&gt;&gt;&gt; n
40234719030664563684489051530416964877785781669439875437823431388841
&gt;&gt;&gt; a
Permutation(0, 25, 32, 15, 8, 28)(1, 48, 34, 14, 10, 51, 38, 31, 21, 5, 42, 47, 29, 26, 46, 30, 50, 49, 37, 22, 18, 23)(2, 45, 17, 20, 36, 40, 11, 4, 7, 41, 33, 3, 43, 44, 19, 16, 35, 39, 12, 6, 9)
&gt;&gt;&gt; Permutation.rank(a) == n
True
</pre>
<p>Now just for fun, let&#8217;s display the permutation above applied to a standard (French) deck of 52 cards. As explained <a href="https://www.johndcook.com/blog/2024/04/30/a-deck-of-cards/">here</a>, symbols associated with these cards have a range of Unicode values. By printing these values, we can visualize the permuted deck.</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/shuffled_deck.png" width="720" height="366" /></p>
<p>Here&#8217;s the code that made the image above.</p>
<pre>spades = list(range(0x1F0A1, 0x1F0AF))
spades.remove(0x1F0AC) # take out the knight
cards = [s + 16*i for s in spades for i in range(4)]

a = Permutation.unrank_lex(52, n)
p = a(cards)

for i in range(4):
    for j in range(13):
        print(chr(p[13*i + j]), end="")
    print()
</pre>
<p>The code above is plenty fast, but Permutation has methods <code>rank_nonlex</code> and <code>unrank_nonlex</code> that run in <em>O</em>(<em>n</em>) time, which could be useful for <em>n</em> much larger than 52.</p>The post <a href="https://www.johndcook.com/blog/2026/07/27/hiding-data-in-permutations/">Hiding data in permutations</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Counting permutations with roots</title>
		<link>https://www.johndcook.com/blog/2026/07/27/counting-permutations-with-roots/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 27 Jul 2026 16:03:44 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Combinatorics]]></category>
		<category><![CDATA[Mathematica]]></category>
		<category><![CDATA[SymPy]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247457</guid>

					<description><![CDATA[<p>My post from yesterday on permutation roots ends with a Mathematica code for finding the probability that a permutation of n elements has a kth root. This is done by finding the coefficient of xn in the generating function I wanted to say more about this, and look at implementing the same code in SymPy. [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/27/counting-permutations-with-roots/">Counting permutations with roots</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>My post from <a href="https://www.johndcook.com/blog/2026/07/26/permutation-roots/">yesterday</a> on permutation roots ends with a Mathematica code for finding the probability that a permutation of <em>n</em> elements has a <em>k</em>th root. This is done by finding the coefficient of <em>x</em><sup><em>n</em></sup> in the generating function</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/expq11.svg" alt="\prod_{m=1}^\infty \exp_{\text{gcd}(m, k)} \left\frac{x^m}{m}\right)" width="162" height="54" /></p>
<p>I wanted to say more about this, and look at implementing the same code in SymPy. I was curious how well SymPy would do because I&#8217;ve noticed that LLMs often generate SymPy code since it&#8217;s an open source <abbr title="computer algebra system">CAS</abbr>.</p>
<p>Wilf [1] describes the infinite product above as the exponential generating function (egf) of <em>f</em>(<em>n</em>, <em>k</em>), the number of permutations of <em>n</em> objects that have a <em>k</em>th root. Since egfs have a <em>n</em>! term in the denominator, this is also the ordinary generating function (ogf) of the <em>probability</em> that a randomly chosen permutation on <em>n</em> objects has a <em>k</em>th root.</p>
<p>My first attempt at using Mathematica to probe the generating function was</p>
<pre>expq[x_, q_] := MittagLefflerE[q, x^q]	 
p[n_, k_] :=  SeriesCoefficient[	 
    Product[expq[x^m/m, GCD[m, k]], {m, 1, Infinity}], {x, 0, n}]
</pre>
<p>This hung forever when I tried to use it on a small example. I realized, but apparently Mathematica did not, that <code>Infinity</code> could be replaced by <code>n</code> since terms higher than <em>n</em> do not contribute to the coefficient of <em>x</em><sup><em>n</em></sup>. With that change, the code ran quickly.</p>
<p>This morning I tried converting the Mathematica code to Sympy; Claude did this in one shot. I also reproduced the table of <em>f</em>(<em>n</em>, <em>k</em>) values on page 150 of [1] to test the code. Since Wilf tabulated <em>f</em>(<em>n</em>, <em>k</em>), not <em>f</em>(<em>n</em>, <em>k</em>)/<em>n</em>!, I multiplied the results by <em>n</em>!.</p>
<p>Here is the output:</p>
<pre>k = 2 [1, 1, 3, 12, 60, 270, 1890, 14280, 128520, 1096200]
k = 3 [1, 2, 4, 16, 80, 400, 2800, 22400, 181440, 1814400]
k = 4 [1, 1, 3, 12, 60, 270, 1890, 13020, 117180, 1039500]
k = 5 [1, 2, 6, 24, 96, 576, 4032, 32256, 290304, 2612736]
k = 6 [1, 1, 1, 4, 40, 190, 1330, 8680, 52920, 340200]
k = 7 [1, 2, 6, 24, 120, 720, 4320, 34560, 311040, 3110400]
</pre>
<p>and here is the SymPy code. I edited the main but the rest is verbatim from Claude.</p>
<pre>from sympy import symbols, gcd, factorial, Rational, S

x = symbols('x')

def expq_coeffs(m, q, n):
    """
    Truncated (degree &lt;= n) series coefficients of
        expq(x**m/m, q) = MittagLefflerE(q, (x**m/m)**q)
    Since q is a positive integer:
        E_q(y^q) = sum_j y^(q*j) / (q*j)!
    with y = x**m/m, so the term of degree m*q*j has coefficient
        1 / ( m**(q*j) * (q*j)! ).
    Returns a list c[0..n] of coefficients.
    """
    c = [S.Zero] * (n + 1)
    j = 0
    while m * q * j &lt;= n:
        deg = m * q * j
        c[deg] += Rational(1, m**(q * j) * factorial(q * j))
        j += 1
    return c

def poly_mult_trunc(a, b, n):
    """Multiply two series (lists of coeffs, index = degree) truncated to degree n."""
    c = [S.Zero] * (n + 1)
    for i, ai in enumerate(a):
        if ai == 0:
            continue
        max_j = n - i
        for j2 in range(max_j + 1):
            bj = b[j2]
            if bj != 0:
                c[i + j2] += ai * bj
    return c

def p(n, k):
    """
    SymPy equivalent of:
        expq[x_, q_] := MittagLefflerE[q, x^q]
        p[n_, k_] := SeriesCoefficient[
            Product[expq[x^m/m, GCD[m, k]], {m, 1, n}], {x, 0, n}]
    """
    result = [S.Zero] * (n + 1)
    result[0] = S.One
    for m in range(1, n + 1):
        q = gcd(m, k)
        factor = expq_coeffs(m, q, n)
        result = poly_mult_trunc(result, factor, n)
    return result[n]

# example
if __name__ == "__main__":
    for k in range(2, 8):
        print("k =", k, [factorial(n)*p(n, k) for n in range(1,11)])
</pre>
<p>[1] Herbert Wilf. Generatingfunctionology. Available online <a href="https://www2.math.upenn.edu/~wilf/DownldGF.html">here</a>.</p>The post <a href="https://www.johndcook.com/blog/2026/07/27/counting-permutations-with-roots/">Counting permutations with roots</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Printing floating point numbers in binary</title>
		<link>https://www.johndcook.com/blog/2026/07/27/float-binary/</link>
					<comments>https://www.johndcook.com/blog/2026/07/27/float-binary/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Mon, 27 Jul 2026 15:34:05 +0000</pubDate>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Math]]></category>
		<category><![CDATA[Number systems]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247449</guid>

					<description><![CDATA[<p>It&#8217;s well known that you can convert the base 16 (hex) representation of an integer to the base 2 (binary) representation by simply converting each digit from hex to binary. For example, CAFEhex = 1100 1010 1111 1110two I imagine it&#8217;s less well known that you can do the same thing with floating point numbers. [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/27/float-binary/">Printing floating point numbers in binary</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s well known that you can convert the base 16 (hex) representation of an integer to the base 2 (binary) representation by simply converting each digit from hex to binary. For example,</p>
<p style="padding-left: 40px;">CAFE<sub>hex</sub> = 1100 1010 1111 1110<sub>two</sub></p>
<p>I imagine it&#8217;s less well known that you can do the same thing with floating point numbers.</p>
<p>I wanted to find the binary representation of a floating point number using Python, and discovered that it has no function to do this. However, there is a method on floats to show a hex representation. For example, here&#8217;s the hex representation of π.</p>
<pre>&gt;&gt;&gt; import math
&gt;&gt;&gt; (math.pi).hex()
'0x1.921fb54442d18p+1'
</pre>
<p>Curiously, the p+<em>k</em> part at the end is an exponent of 2, not an exponent of 16. So after we convert 1.921fb54442d18 to binary, we&#8217;ll need to multiply by 2, i.e. move the fractional point one space to the right.</p>
<p>So first we convert 1.921fb54442d18<sub>hex</sub> to binary by converting 1, 9, 2, etc. each to binary.</p>
<p style="padding-left: 40px;">1.1001 0010 0001 1111 1011 0101 0100 0100 0100 0010 1101 0001 1000<sub>two</sub></p>
<p>Then after shifting the fraction point to account for the <code>p+1</code> part we have</p>
<p style="padding-left: 40px;">π = 11.001001000011111101101010100010001000010110100011000<sub>two</sub></p>
<p>You could use Python&#8217;s bin() function to convert the fractional part, interpreted as an integer, to hex, though you may need to pad with 0 bits. For example,</p>
<pre>&gt;&gt;&gt; (1.03).hex()
'0x1.07ae147ae147bp+0

&gt;&gt;&gt; bin(0x7ae147ae147)
'0b1111010111000010100011110101110000101000111'
</pre>
<p>The binary representation of 1.03<sub>ten</sub> is</p>
<p style="padding-left: 40px;">1.000001111010111000010100011110101110000101000111<sub>two</sub></p>
<p>We added a total of five zero bits, four for the 0 after the fractional point and one for converting 7 to 0111<sub>two</sub>.</p>The post <a href="https://www.johndcook.com/blog/2026/07/27/float-binary/">Printing floating point numbers in binary</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/27/float-binary/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Permutation roots</title>
		<link>https://www.johndcook.com/blog/2026/07/26/permutation-roots/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 26 Jul 2026 20:32:02 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247440</guid>

					<description><![CDATA[<p>Let σ be a permutation on n elements. If there is a permutation τ such that applying τ twice has the same effect on the list of elements as applying σ once, we say σ = τ² and τ is a square root of σ. If we let our n elements be the integers 0 [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/26/permutation-roots/">Permutation roots</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Let σ be a permutation on <em>n</em> elements. If there is a permutation τ such that applying τ twice has the same effect on the list of elements as applying σ once, we say σ = τ² and τ is a square root of σ.</p>
<p>If we let our <em>n</em> elements be the integers 0 through <em>n</em> − 1, then we can represent permutations by what they do to this list of numbers. In Python as a tuple of length <em>n</em> and compose permutations with the following function:</p>
<pre>import itertools

def compose(sigma, tau):
    "Return the composition σ ∘ τ (apply τ first, then σ)."
    return tuple(sigma[j] for j in tau)
</pre>
<p>We can always construct permutations that have square roots by squaring a permutation. If we run the following code</p>
<pre>tau = (3, 1, 4, 5, 2, 0)
sigma = compose(tau, tau)
</pre>
<p>we find σ = (5, 1, 2, 0, 4, 3), and by construction (3, 1, 4, 5, 2, 0) is a square root of &amp;sigma, though it&#8217;s not the only one.</p>
<p>The following code shows that σ has four roots.</p>
<pre>import itertools

def numroots(sigma):
    n = len(sigma)
    c = 0
    for tau in itertools.permutations(range(n)):
        if sigma == compose(tau, tau):
            c += 1
    return c

print( numroots(sigma) )
print( numroots( (1, 2, 3, 4, 5, 0) ) )
</pre>
<p>It also shows that the rotation (1, 2, 3, 4, 5. 0) has no roots.</p>
<p>The function <code>numroots</code> has runtime proportional to <em>n</em>! and so it&#8217;s not practical for large permutations. There is a theorem that says a permutation σ has a square root if and only if the number of cycles it has of every even length is even. See [1].</p>
<p>We can also define cubes and cube roots of permutations, and higher powers and roots.</p>
<p>How common is it for permutations to have square roots, or cube roots, etc.? If you pick a random permutation on <em>n</em> elements, what is the probability that it has a <em>k</em>th root?</p>
<p>This is a hard question in general, but it is equivalent to finding the coefficient of <em>x</em><sup><em>k</em></sup> in the infinite product</p>
<p><img loading="lazy" decoding="async" class="aligncenter size-medium" src="https://www.johndcook.com/expq11.svg" alt="\prod_{m=1}^\infty \exp_{\text{gcd}(m, k)} \left(\frac{x^m}{m}\right)" width="162" height="54" /></p>
<p>This is theorem 4.8.3 in [1]. This theorem was the motivation for writing about exp<sub><em>q</em></sub> in the <a href="https://www.johndcook.com/blog/2026/07/26/exp-q/">previous post</a>.</p>
<p>Although the product is infinite, there&#8217;s no need to compute terms in the product that only contribute powers of <em>x</em> higher than you&#8217;re interested in. The following Mathematica code will compute the probability that a permutation on <em>n</em> elements has a <em>k</em>th root.</p>
<pre>expq[x_, q_] := MittagLefflerE[q, x^q]	 
p[n_, k_] :=  SeriesCoefficient[	 
    Product[expq[x^m/m, GCD[m, k]], {m, 1, n}], {x, 0, n}]
</pre>
<p>So, for example, the probability that a permutation of 10 elements has a square root is 29/96.</p>
<p>[1] Herbert Wilf. Generatingfunctionology. Available online <a href="https://www2.math.upenn.edu/~wilf/DownldGF.html">here</a>.</p>The post <a href="https://www.johndcook.com/blog/2026/07/26/permutation-roots/">Permutation roots</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>exp_q</title>
		<link>https://www.johndcook.com/blog/2026/07/26/exp-q/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sun, 26 Jul 2026 18:20:36 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Differential equations]]></category>
		<category><![CDATA[Special functions]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247432</guid>

					<description><![CDATA[<p>The function expq(x) is defined by taking the power series for exp(x) and keeping only the terms whose index is a multiple of q. For example, exp2(x) keeps only the even-numbered terms in the exponential power series and so equals cosh(x). In general, The first sum uses Iverson&#8217;s bracket notation: a Boolean expression in brackets [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/26/exp-q/">exp_q</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>The function exp<sub><em>q</em></sub>(<em>x</em>) is defined by taking the power series for exp(<em>x</em>) and keeping only the terms whose index is a multiple of <em>q</em>. For example, exp<sub>2</sub>(<em>x</em>) keeps only the even-numbered terms in the exponential power series and so equals cosh(<em>x</em>).</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expqx.svg" alt="\exp_2(x) = 1 + \frac{x^2}{2!} + \frac{x^4}{4!} + \frac{x^6}{6!} + \cdots = \cosh(x)" width="359" height="44" /></p>
<p>In general,</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq2.svg" alt="\exp_q(x) = \sum_{n=0}^\infty [q \mid n] \frac{x^n}{n!} = \sum_{n=0}^\infty \frac{x^{nq}}{(nq)!}" width="285" height="54" /></p>
<p>The first sum uses <a href="https://www.johndcook.com/blog/2023/07/01/activation-functions/">Iverson&#8217;s bracket notation</a>: a Boolean expression in brackets denotes the function that returns 1 when the expression is true and zero when it is false. Here the bracket equals 1 when <em>q</em> divides <em>n</em> and is zero otherwise.</p>
<h2>Closed forms</h2>
<p>Let ω = exp(2π<em>i</em> / <em>q</em>). Then</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq3.svg" alt="\exp_q(x) = \frac{1}{q}\sum_{k=0}^{q-1} \exp(\omega^k x)" width="209" height="60" /></p>
<p>This lets us find closed-form expressions for exp<sub><em>q</em></sub>(<em>x</em>). For example, when <em>q</em> = 4, ω = <em>i</em> and</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq4.svg" alt="\exp_4(x) = \frac{1}{2}\left( \cosh(x) + \cos(x) \right)" width="257" height="40" /></p>
<p>Here&#8217;s a proof of the identity above:</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq5.svg" alt="\begin{align*} \frac{1}{q} \sum_{k=0}^{q-1} \exp(\omega^k x) &amp;= \frac{1}{q} \sum_{k=0}^{q-1} \sum_{n=0}^\infty \frac{\omega^{kn}x^n}{n!} \\ &amp;= \sum_{n=0}^\infty \left( \frac{1}{q} \sum_{k=0}^{q-1} \omega^{kn}\right) \frac{x^n}{n!} \\ &amp;= \sum_{n=0}^\infty [q \mid n] \frac{x^n}{n!} \\ &amp;= \exp_q(x) \end{align*} " width="296" height="230" /></p>
<p>In the proof we used the identity</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq6.svg" alt="\frac{1}{q} \sum_{k=0}^{q-1} \omega^{kn} = [q \mid n]" width="150" height="60" /></p>
<p>which is important in deriving the properties of the discrete Fourier transform.</p>
<h2>Differential equations</h2>
<p>The first time I saw the function exp<sub><em>q</em></sub>(<em>x</em>) was in differential equations, though I didn&#8217;t know at the time the function had a name.</p>
<p>When a course in differential equations gets to power series solutions, a common example or homework problem is to solve</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq7.svg" alt="y^{(k)}(x) = y(x)" width="117" height="23" /></p>
<p>for <em>k</em> = 3 or 4, i.e. to find a function that equals its third or fourth derivative.</p>
<p>If the initial conditions are</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq8.svg" alt="y(0) = 0" width="70" height="18" /></p>
<p>and</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq9.svg" alt="y^\prime(0) = y^{\prime\prime}(0) = \cdots = y^{(k-1)}(0) = 0" width="285" height="23" /></p>
<p>the unique solution to</p>
<p><img loading="lazy" decoding="async" class="aligncenter" style="background-color: white;" src="https://www.johndcook.com/expq7.svg" alt="y^{(k)}(x) = y(x)" width="117" height="23" /></p>
<p>is <em>y</em>(<em>x</em>) = exp<sub><em>k</em></sub>(<em>x</em>).</p>
<h2>Mathematica and Mittag-Leffler</h2>
<p>Mathematica does not have a built-in function implementing exp<sub><em>q</em></sub>(<em>x</em>), but it does have an implementation of the <a href="https://www.johndcook.com/blog/2016/07/17/mittag-leffler-function-and-probability-distribution/">Mittag-Leffler function</a>, and so thanks to a relation between this function and exp<sub><em>q</em></sub>(<em>x</em>) you can implement the latter as</p>
<pre>expq[x_, q_] := MittagLefflerE[q, x^q]</pre>
<h2>Combinatorics</h2>
<p>The first time I saw the <em>notation</em> exp<sub><em>q</em></sub>(<em>x</em>) was in combinatorics. I had intended to include an application from that book here, but I make that the topic for the <a href="https://www.johndcook.com/blog/2026/07/26/permutation-roots/">next post</a>.</p>The post <a href="https://www.johndcook.com/blog/2026/07/26/exp-q/">exp_q</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Excel column numbering</title>
		<link>https://www.johndcook.com/blog/2026/07/25/excel-column-numbering/</link>
					<comments>https://www.johndcook.com/blog/2026/07/25/excel-column-numbering/#comments</comments>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Sat, 25 Jul 2026 13:37:30 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number systems]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247423</guid>

					<description><![CDATA[<p>I was working with a wide spreadsheet from a client the other day and I had to convert between Excel column labels and column numbers. I had never paid attention to how Excel labels columns and implicitly thought it was base 26 using letters rather than digits. But then I realized that&#8217;s not right. Excel [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/25/excel-column-numbering/">Excel column numbering</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>I was working with a wide spreadsheet from a client the other day and I had to convert between Excel column labels and column numbers. I had never paid attention to how Excel labels columns and implicitly thought it was base 26 using letters rather than digits. But then I realized that&#8217;s not right.</p>
<p>Excel labels columns A through Z, then AA through AZ, then BA through BZ, etc. If this is base 26, then does A correspond to 0? That could work for A through Z, but then what about AA? Then you&#8217;d have to say the first A corresponds to 26 but the second A corresponds to 0.</p>
<p>Does Z correspond to 0? If so then the column numbers would be 1 through 25, followed by 0, then 27. And it would mean that columns ZA through ZZ are the same as A through Z.</p>
<p>In fact nothing in Excel column labeling corresponds to 0. The labels cannot be interpreted as a positional number system.</p>
<p>There&#8217;s a name for this kind of number system: <strong>bijective base 26</strong>. The concept extends generally to bijective base <em>b</em> for any positive integer <em>b</em>. The idea is ancient, but the name was coined recently. It has also been called <em>k</em>-adic numbering. For most of history it didn&#8217;t have a name.</p>
<p>The motivation behind the name bijective base <em>b</em> is that there is a bijection (a one-to-one correspondence) between these symbols and positive integers; there&#8217;s no possibility of leading zeros that would keep the mapping from being a bijection, unlike say 7 and 07 representing the same number.</p>
<h2>Excel limits</h2>
<p>Before 2007, an Excel file could have a maximum of 2<sup>8</sup> = 256 columns, and so the largest column label was IV.</p>
<p>Then in 2007 the column limit was increased to 2<sup>14</sup> = 16,384 and the largest column label is XFD.</p>
<h2>Conversion code</h2>
<p>Converting from column labels to integers is easy; going the other way is a little more complicated.</p>
<pre>letter_to_ordinal = lambda c: ord(c) - ord('A') + 1
ordinal_to_letter = lambda n: chr(ord('A') + n - 1)

def label_to_num(label):
    label = label.upper()
    n = 0
    for c in label:
        n = n*26 + letter_to_ordinal(c)
    return n

def num_to_label(n):
    letters = []
    while n &gt; 0:
        n, remainder = divmod(n - 1, 26)
        letters.append(ordinal_to_letter(remainder + 1))
    return ''.join(reversed(letters))
</pre>
<p>Here&#8217;s an <a href="https://www.johndcook.com/excel_labels.html">online calculator</a> based on the code above.</p>
<h2>Tests</h2>
<p>The following code verifies the assertions above about the maximum number of Excel columns over time.</p>
<pre>assert(num_to_label(256) == "IV")
assert(label_to_num("IV") == 256)

assert(num_to_label(2**14) == "XFD")
assert(label_to_num("XFD") == 2**14)
</pre>
<p>The conversion routines are not limited to actual Excel labels but work for arbitrarily large integers and bijective base 26 representations. For example, the following code shows that the bijective base 26 representation of Avogadro&#8217;s number is MUAEKAUDYDXEWOSDD.</p>
<pre>avogadro = 602_214_076_000_000_000_000_000
assert(label_to_num(num_to_label(avogadro)) == avogadro)
print(num_to_label(avogadro))
</pre>
<h2>Related posts</h2>
<ul>
<li class="link"><a href="https://www.johndcook.com/radix_conversion.html">Radix conversion calculator</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2020/03/16/round-trip-radix-conversion/">When is floating point radix conversion exact?</a></li>
<li class="link"><a href="https://www.johndcook.com/blog/2019/09/07/excel-r-bom/">Excel, R, and Unicode</a></li>
</ul>The post <a href="https://www.johndcook.com/blog/2026/07/25/excel-column-numbering/">Excel column numbering</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
					<wfw:commentRss>https://www.johndcook.com/blog/2026/07/25/excel-column-numbering/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>An almost periodic function</title>
		<link>https://www.johndcook.com/blog/2026/07/23/an-almost-periodic-function/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Thu, 23 Jul 2026 14:04:34 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247418</guid>

					<description><![CDATA[<p>This post takes a more abstract view of the previous post. That post looked at the concrete question of whether a number ever has the same sine in radians as in degrees. The relation between radians and degrees is irrelevant except that π/180 is an irrational number. Suppose α and β are two positive numbers [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/23/an-almost-periodic-function/">An almost periodic function</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>This post takes a more abstract view of the <a href="https://www.johndcook.com/blog/2026/07/22/degrees-radians/">previous post</a>. That post looked at the concrete question of whether a number ever has the same sine in radians as in degrees. The relation between radians and degrees is irrelevant except that π/180 is an irrational number.</p>
<p>Suppose α and β are two positive numbers such that α/β is irrational. In the previous post, α = 1 and β = π/180. Then the function</p>
<p style="padding-left: 40px;"><em>f</em>(<em>x</em>) = sin(α<em>x</em>) − sin(β<em>x</em>)</p>
<p>is almost periodic: it is not periodic, but it comes close to being periodic, as close as you&#8217;d like provided you&#8217;re willing to look over a sufficiently long range of <em>x</em>&#8216;s.</p>
<p>The identity</p>
<p style="padding-left: 40px;">sin(α<em>x</em>) − sin(β<em>x</em>) = 2 cos((α + β)<em>x</em>/2) sin((α − β)<em>x</em>/2)</p>
<p>shows that <em>f</em>(<em>x</em>) is the product of two periodic functions but is not periodic itself. The periods of the cosine and sine above never coincide because the ratio of their frequencies is irrational.</p>
<p>The zeros of <em>f</em> are not periodic, though they can be divided into two subsequences that are periodic.</p>The post <a href="https://www.johndcook.com/blog/2026/07/23/an-almost-periodic-function/">An almost periodic function</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>When sine of x degrees equals sine of x radians</title>
		<link>https://www.johndcook.com/blog/2026/07/22/degrees-radians/</link>
		
		<dc:creator><![CDATA[John]]></dc:creator>
		<pubDate>Wed, 22 Jul 2026 12:17:11 +0000</pubDate>
				<category><![CDATA[Math]]></category>
		<category><![CDATA[Number theory]]></category>
		<guid isPermaLink="false">https://www.johndcook.com/blog/?p=247407</guid>

					<description><![CDATA[<p>Ordinarily the sine of x radians and the sine of x degrees are very different numbers. Having your calculator in radian mode when it should be in degree mode, or vice versa, results in a major error. But sometimes it doesn&#8217;t matter. A trivial example is when x = 0. A more interesting example is [&#8230;]</p>
The post <a href="https://www.johndcook.com/blog/2026/07/22/degrees-radians/">When sine of x degrees equals sine of x radians</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></description>
										<content:encoded><![CDATA[<p>Ordinarily the sine of <em>x</em> radians and the sine of <em>x</em> degrees are very different numbers. Having your calculator in radian mode when it should be in degree mode, or vice versa, results in a major error.</p>
<p>But sometimes it doesn&#8217;t matter. A trivial example is when <em>x</em> = 0. A more interesting example is</p>
<p style="padding-left: 40px;"><em>x</em> = 180π/(180 + π) = 3.08770208….</p>
<p>For that value of <em>x</em>,</p>
<p style="padding-left: 40px;">sin(<em>x</em>) = sin(<em>x</em>°).</p>
<p>In this article I&#8217;ll use the common convention of using radians by default and denoting degrees with ° as above.</p>
<p>Note that</p>
<p style="padding-left: 40px;"><em>x</em><em> = </em>π<em>x</em><em>°</em>/180</p>
<p>and so we are interested in solutions to the equation</p>
<p style="padding-left: 40px;">sin(<em>x</em>) = sin(π<em>x</em>/180)</p>
<p>Now two angles <em>A</em> and <em>B</em> have the same sine if they differ by a multiple of 2π, or if they&#8217;re supplementary (i.e. <em>A</em> = π − <em>B</em>), or both. To put it another way, if <em>A</em> and <em>B</em> have the same sine, they are either equal mod 2π or supplementary mod 2π. This means that</p>
<p style="padding-left: 40px;">sin(<em>x</em>) = sin(π<em>x</em>/180)</p>
<p>if and only if</p>
<p style="padding-left: 40px;"><em>x</em> = π<em>x</em>/180 + 2π<em>k</em></p>
<p>or</p>
<p style="padding-left: 40px;"><em>x</em> = π − π<em>x</em>/180 + 2π<em>k</em></p>
<p>for some integer <em>k</em>.</p>
<p>Therefore all solutions have the form</p>
<p style="padding-left: 40px;"><em>x</em> = 360π<em>k</em>/(180 − π)</p>
<p>or</p>
<p style="padding-left: 40px;"><em>x</em> = 180π(2<em>k</em> + 1)/(180 + π).</p>
<h2>Alternative solution</h2>
<p>The derivation above is correct, but it occurred to me later that a simpler argument would be to use the identity</p>
<p style="padding-left: 40px;">sin(<em>A</em>) − sin(<em>B</em>) = 2 cos((<em>A</em> + <em>B</em>)/2) sin((<em>A</em> − <em>B</em>)/2).</p>
<p>Thus <em>A</em> and <em>B</em> have the same sine if</p>
<p style="padding-left: 40px;">cos((<em>A</em> + <em>B</em>)/2) = 0</p>
<p>or if</p>
<p style="padding-left: 40px;">sin((<em>A</em> − <em>B</em>)/2) = 0.</p>
<p>These two possibilities correspond to the two families of solutions above.</p>
<h2>Density</h2>
<p>When reduced modulo 2π, both families are dense in [0, 2π]. This means that for every <em>y</em> in [−1, 1], there is a number <em>x</em> such that</p>
<p style="padding-left: 40px;">sin(<em>x</em>) = sin(<em>x</em>°) ≈ <em>y</em></p>
<p>and we can make the approximation as good as we&#8217;d like.</p>
<h3>Example 1</h3>
<p>For example, today is July 22, so let&#8217;s set <em>y</em> = 0.722. We&#8217;d like to find a value of <em>x</em> such that the sine of <em>x</em> radians and the sine of <em>x</em> degrees both approximately equal 0.722. And let&#8217;s say our approximation tolerance is ε = 0.0001.</p>
<p>We can search for a value of <em>x</em> in the first family of solutions by looking for a value of <em>k</em> with</p>
<p style="padding-left: 40px;">| sin(360π<em>k</em>/(180 − π)) − 0.722 | &lt; 0.0001</p>
<p>and the smallest such <em>k</em> is 96343 and so</p>
<p style="padding-left: 40px;"><em>x</em> = 360×96343 π/(180 − π) = 616093.78713621…</p>
<p>will do, and sin(<em>x</em>) = 0.72191…</p>
<h3>Example 2</h3>
<p>Now let&#8217;s set <em>y</em> = 0.2026 and look for a solution in the other family of solutions, and this time let&#8217;s set ε = 10<sup>−6</sup>. The smallest value of <em>k</em> such that</p>
<p style="padding-left: 40px;">| sin(180π(2<em>k</em> + 1)/(180 + π)) − 0.2026 | &lt; 10<sup>−6</sup></p>
<p>is <em>k</em> = 741141. Then</p>
<p style="padding-left: 40px;">sin( 4576848.310950611 ) = sin( 4576848.310950611° ) = 0.202600139…</p>The post <a href="https://www.johndcook.com/blog/2026/07/22/degrees-radians/">When sine of x degrees equals sine of x radians</a> first appeared on <a href="https://www.johndcook.com/blog">John D. Cook</a>.]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
