<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>LCG on Dear Fortuna</title><link>http://dearfortuna.blog/tags/lcg/</link><description>Recent content in LCG on Dear Fortuna</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>© 2026 Vasyl Bodnar | Text: CC BY 4.0 | Code: BSD-3</copyright><lastBuildDate>Mon, 09 Feb 2026 22:00:00 -0400</lastBuildDate><atom:link href="http://dearfortuna.blog/tags/lcg/index.xml" rel="self" type="application/rss+xml"/><item><title>Nicest (small) LCG numbers</title><link>http://dearfortuna.blog/posts/nicest-lcg/</link><pubDate>Mon, 09 Feb 2026 22:00:00 -0400</pubDate><guid>http://dearfortuna.blog/posts/nicest-lcg/</guid><description>&lt;h2 id="intro">Intro&lt;/h2>
&lt;p>Recently I wanted to give a nice small example of a &lt;a href="https://en.wikipedia.org/wiki/Linear_congruential_generator">Linear Congruential Generator&lt;/a> (LCG)
to show how easy and simple &lt;a href="https://en.wikipedia.org/wiki/Pseudorandom_number_generator">PseudoRandom Number Generators&lt;/a> (PRNG) are.&lt;/p>
&lt;p>An LCG is typically &lt;code>x[i] = a * x[i - 1] + c (mod m)&lt;/code>, where &lt;code>x[0]&lt;/code> is the initial seed, &lt;code>a&lt;/code> is multiplier, &lt;code>c&lt;/code> is additive, &lt;code>m&lt;/code> is our space.&lt;/p>
&lt;p>There are many good numbers online, and even plenty of bad numbers have a decent enough period, i.e. when the sequence starts repeating.
You will not notice the issue immediately in many of those cases, even if a computer or a bad actor would.
LCGs are simple and efficient, but are one of the least cryptographically secure after all.&lt;/p></description><content:encoded><![CDATA[<h2 id="intro">Intro</h2>
<p>Recently I wanted to give a nice small example of a <a href="https://en.wikipedia.org/wiki/Linear_congruential_generator">Linear Congruential Generator</a> (LCG)
to show how easy and simple <a href="https://en.wikipedia.org/wiki/Pseudorandom_number_generator">PseudoRandom Number Generators</a> (PRNG) are.</p>
<p>An LCG is typically <code>x[i] = a * x[i - 1] + c (mod m)</code>, where <code>x[0]</code> is the initial seed, <code>a</code> is multiplier, <code>c</code> is additive, <code>m</code> is our space.</p>
<p>There are many good numbers online, and even plenty of bad numbers have a decent enough period, i.e. when the sequence starts repeating.
You will not notice the issue immediately in many of those cases, even if a computer or a bad actor would.
LCGs are simple and efficient, but are one of the least cryptographically secure after all.</p>
<p>The issue is that LCG numbers are frequently large.
They have to be for good randomness over a large space, e.g. 2^32, the typical size of an <code>int</code>.
This does form a good question though, what would be the nicest LCG numbers, i.e. still looks &ldquo;random&rdquo;, yet small (under a 100 preferably).</p>
<h2 id="brute-forcing-into-n">Brute Forcing into N</h2>
<p>Now I am certain that there are analytical methods to find good numbers with the properties that I want.
I would not be surprised if someone did that already either.
But, these numbers are small, I can quite literally just loop over the entire input space and pick the best ones.
We do need a scoring function to check how good the inputs are though.</p>
<p>Let&rsquo;s start with uniqueness as our scoring function.
That is, we just check whether a number occurs for each possible number.
This rewards long periods and using all of our numbers as any future period or identical numbers will not impact the score.</p>
<p>Let us check all numbers up to (but excluding) 12.</p>
<p>The best result we get is <code>m = 11</code>, <code>c = 1</code>, <code>a = 1</code>, <code>x[0] = 1</code> or:</p>





<pre tabindex="0"><code>1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 ...</code></pre><p>Oh how great and random it is, most would almost certainly disagree.
The sequence is not ideal in what we usually think of as &ldquo;random&rdquo;.</p>
<p>Yeah, I should have expected that.
Despite not looking random, it has the best period of 11, known as full period (<code>= m</code>).
It even uses all of our numbers!</p>
<p>What about second or third or fifteenth best?
Just shifted, you get tweaks to <code>c</code> or <code>x[0]</code>, but it is practically the same obvious ordered sequence.</p>
<p>The flaw is in the scoring system as it does not care about anything except uniqueness.
Indeed, to it <code>1 2 3 4 5</code> and <code>4 2 5 3 1</code> are identical as long as same numbers are involved,
even if to us the second is more &ldquo;random&rdquo;.</p>
<h2 id="discouraging-unfair-play">Discouraging Unfair Play</h2>
<p>Well the core issue is that the algorithm can set <code>a = 1</code>, <code>m = 11</code> and
tweak <code>c</code> and <code>x[0]</code> with a simple ordered sequence.
One way to deal with it somewhat is to check whether the difference between numbers changes.
If it does not, reduce the score for each violation.</p>
<p>This system can have trouble tracking changes modulo.
E.g. <code>10 0 1</code>, has differences of <code>10</code> and <code>1</code>, rather than <code>1 mod 11</code> for both.
But we can just ignore those and look at other options. I call this technology Human-In-The-Loop™.</p>
<p>The results look much better:</p>





<pre tabindex="0"><code>x[0] = 5, a = 1, c = 5, m = 11 :: 5 10 4 9 3 8 2 7 1 6 0 5 ...
x[0] = 5, a = 1, c = 6, m = 11 :: 5 0 6 1 7 2 8 3 9 4 10 5 ...
x[0] = 1, a = 2, c = 0, m = 11 :: 1 2 4 8 5 10 9 7 3 6 1 2 ...
x[0] = 1, a = 6, c = 0, m = 11 :: 1 6 3 7 9 10 5 8 4 2 1 6 ...
x[0] = 1, a = 7, c = 0, m = 11 :: 1 7 5 2 3 10 4 6 9 8 1 7 ...
x[0] = 1, a = 8, c = 0, m = 11 :: 1 8 9 6 4 10 3 2 5 7 1 8 ...
x[0] = 1, a = 2, c = 1, m = 11 :: 1 3 7 4 9 8 6 2 5 0 1 3  ...
x[0] = 1, a = 6, c = 1, m = 11 :: 1 7 10 6 4 3 8 5 9 0 1 7 ...
x[0] = 1, a = 7, c = 1, m = 11 :: 1 8 2 4 7 6 10 5 3 0 1 8 ...
x[0] = 1, a = 8, c = 1, m = 11 :: 1 9 7 2 6 5 8 10 4 0 1 9 ...</code></pre><p>The first two do only tweak <code>c</code> and <code>x[0]</code>, but they don&rsquo;t look too bad in comparison to <code>1 2 3 4 ...</code>.
Also <code>x[0]</code> is set to <code>1</code>, but this is more of a first mover advantage (my brute force loop starts with <code>x[0] = 1</code>), and this is a seed to begin with.
I wouldn&rsquo;t mind using these, but it would be useful to test it on more numbers.</p>
<h2 id="larger-numbers">Larger numbers</h2>
<p>Let us then try up to (including) 50, this takes a second, but does find me some interesting results:</p>





<pre tabindex="0"><code>x[0] = 1, a = 11, c = 1, m = 50 :: 1 12 33 14 5 6 17 38 19 10 11 22 ...
x[0] = 1, a = 21, c = 1, m = 50 :: 1 22 13 24 5 6 27 18 29 10 11 32 ...
x[0] = 1, a = 31, c = 1, m = 50 :: 1 32 43 34 5 6 37 48 39 10 11 42 ...
x[0] = 1, a = 41, c = 1, m = 50 :: 1 42 23 44 5 6 47 28 49 10 11 2  ...
x[0] = 1, a = 11, c = 3, m = 50 :: 1 14 7 30 33 16 29 22 45 48 31   ...
x[0] = 1, a = 21, c = 3, m = 50 :: 1 24 7 0 3 16 39 22 15 18 31 4   ...
x[0] = 1, a = 31, c = 3, m = 50 :: 1 34 7 20 23 16 49 22 35 38 31   ...
x[0] = 1, a = 41, c = 3, m = 50 :: 1 44 7 40 43 16 9 22 5 8 31 24   ...
x[0] = 1, a = 11, c = 7, m = 50 :: 1 18 5 12 39 36 3 40 47 24 21    ...
x[0] = 1, a = 21, c = 7, m = 50 :: 1 28 45 2 49 36 13 30 37 34 21   ...</code></pre><p>It of course prefers <code>50</code> as <code>m</code>, since that provides the largest period.
The biggest issue is that <code>a = 11</code> and <code>+10</code> cousins have their issues, especially for <code>c = 1</code>.
It is a little off to see so many multiples of <code>11</code>. Likely due to <code>m = 50</code>.
But with <code>c = 7</code> or even <code>c = 3</code>, it looks decent enough.
I will have to look into a way to improve it by adding more to the score.</p>
<p>I feel that while <code>m = 50</code> is great for long periods, it is not good for &ldquo;randomness&rdquo;.
So that is another avenue for improvement.</p>
<p>We can also look at <code>m = 61</code> instead of <code>m = 50</code> as it might be nicer:</p>





<pre tabindex="0"><code>x[0] = 1, a = 26, c = 0, m = 61 :: 1 26 5 8 25 40 3 17 15 24 14 59 9  ...
x[0] = 1, a = 30, c = 0, m = 61 :: 1 30 46 38 42 40 41 10 56 33 14 54 ...
x[0] = 1, a = 31, c = 0, m = 61 :: 1 31 46 23 42 21 41 51 56 28 14 7  ...
x[0] = 1, a = 35, c = 0, m = 61 :: 1 35 5 53 25 21 3 44 15 37 14 2 9  ...
x[0] = 1, a = 43, c = 0, m = 61 :: 1 43 19 24 56 29 27 2 25 38 48 51  ...
x[0] = 1, a = 44, c = 0, m = 61 :: 1 44 45 28 12 40 52 31 22 53 14 6  ...
x[0] = 1, a = 51, c = 0, m = 61 :: 1 51 39 37 57 40 27 35 16 23 14 43 ...
x[0] = 1, a = 54, c = 0, m = 61 :: 1 54 49 23 22 29 41 18 57 28 48 30 ...
x[0] = 1, a = 55, c = 0, m = 61 :: 1 55 36 28 15 32 52 54 42 53 48 17 ...
x[0] = 1, a = 59, c = 0, m = 61 :: 1 59 4 53 16 29 3 55 12 37 48 26 9 ...</code></pre><p>I look at bigger <code>a</code>s as small ones give obvious powers.
These look alright, but have their issues too.</p>
<h2 id="final-results">Final Results</h2>
<p>My quest continues for the better numbers.
There is also a limit to how many inputs I can handle,
so maybe I should consider using some better state exploration than &ldquo;loop over everything&rdquo;.
Naturally more work required for a better scoring system.</p>
]]></content:encoded></item></channel></rss>