(19-02-2026, 01:29 PM)nablator wrote: You are not allowed to view links. Register or Login to view.Thank you for the answer and code. Again, I'm sorry, I have near-zero knowledge of Python and its libraries so someone better qualified should comment.
Let me explain the code:
First, it creates two aligned sequences:
x = all characters except the last d
y = all characters except the first d
zip(x, y) simply pairs elements from the two lists:
If:
x = [A, B, C, D]
y = [D, E, F, G]
then:
zip(x, y) gives:
(A, D)
(B, E)
(C, F)
(D, G)
np.bincount counts how many times each character appears (so we have how many times a character appears in x and in y)
joint[(a,b)] += 1 counts how often each pair appears. That gives us the joint frequency distribution.
Alpha is just a small number added to the character counts. If a character never appears in one of the positions, its count would be zero. But as I later take logarithms and logarithm of zero is undefined, I add a small constant (like 0.5) to every count before turning counts into probabilities. This is called smoothing. It does not significantly change results for large texts.
Then I just calculate mi with mi += pxy * (log(pxy) - log(px[a]) - log(py[b]))
It compares: “How often does this pair actually occur?” with “How often would it occur if the two positions were independent?” If the actual joint frequency differs from what independence predicts, MI becomes positive.
The function calculates one single mutual information value for a given distance d, based on all pairs at that distance. So, in the code I iterate for each d in a loop out of the function to get the plot data.



![[+]](https://www.voynich.ninja/images/collapse_collapsed.png)