Generalizing TOTP Anagram Counting for Different Bases

Math
Anagrams
Combinatorics
TOTP
OEIS
Author

Jean-François Im

Published

April 12, 2026

While working on the submission for A394980 — the count of anagram pairs in halves of TOTP codes — one of my more mathematically inclined friends asked if this was just a different series in disguise. I couldn’t find any other matching sequences initially, but when working on it, the other dimension that I didn’t explore initially was the assumption that one is working with base 10, as would be the case for regular TOTP codes. Changing the base to different values is where the plot thickens.

Changing the code to use different values for the base is relatively trivial, so for bases 2 to 10, the first 25 terms are:

Code
from itertools import product, combinations_with_replacement
from collections import Counter
from math import factorial, perm
import time

def distinct_perms(seq):
    """Number of distinct permutations of seq."""
    counts = Counter(seq)
    denom = 1
    for k in counts.values():
        denom *= factorial(k)
    val = factorial(len(seq)) // denom
    return val

def partitions(n, max_part=None):
    """Generate all integer partitions of n."""
    if max_part is None:
        max_part = n
    if n == 0:
        yield []
        return
    for first in range(min(n, max_part), 0, -1):
        for rest in partitions(n - first, first):
            yield [first] + rest

def partition_freq(p, base=10):
    """Number of distinct prefixes of length n with the shape described by partition p."""
    num = perm(base, len(p))
    group_sizes = Counter(p).values()
    denom = 1
    for g in group_sizes:
        denom *= factorial(g)
    return num // denom

def expand(p):
    """Turns a partition into a sequence, eg. [4,1,1] -> [1,1,1,1,2,3]"""
    result = []
    for i, count in enumerate(p):
        result.extend([i] * count)
    return result

for base in range(2,11):
    seq = [sum(partition_freq(p, base) * (distinct_perms(expand(p))**2) for p in partitions(n)) for n in range(1,26)]
    print(f'b={base}: {",".join(map(str, seq))}')
b=2: 2,6,20,70,252,924,3432,12870,48620,184756,705432,2704156,10400600,40116600,155117520,601080390,2333606220,9075135300,35345263800,137846528820,538257874440,2104098963720,8233430727600,32247603683100,126410606437752
b=3: 3,15,93,639,4653,35169,272835,2157759,17319837,140668065,1153462995,9533639025,79326566595,663835030335,5582724468093,47152425626559,399769750195965,3400775573443089,29016970072920387,248256043372999089,2129158861755559587,18301184259810988815,157626238006835196237,1360135024740956026161,11756409817108040588403
b=4: 4,28,256,2716,31504,387136,4951552,65218204,878536624,12046924528,167595457792,2359613230144,33557651538688,481365424895488,6956365106016256,101181938814289564,1480129751586116848,21761706991570726096,321401321741959062016,4766118425002290943216,70937237822612227230784,1059325264441498545599488,15867288561475917552050176,238332503119317701296868416,3588998891197749315801781504
b=5: 5,45,545,7885,127905,2241225,41467725,798562125,15855173825,322466645545,6687295253325,140927922498025,3010302779775725,65046639827565525,1419565970145097545,31249959913055650125,693192670456484513025,15480814434909059226825,347819791175860063294125,7857219150562637891693385,178365050610159455909952525,4067061957851359126795514325,93113307750242799253647686025,2139705756334600522205487928425,49337502810931248099790152915405
b=6: 6,66,996,18306,384156,8848236,218040696,5651108226,152254667436,4229523740916,120430899525096,3499628148747756,103446306284890536,3102500089343886696,94219208840385966096,2892652835496484004226,89662253086458906345036,2802887038905690746642916,88285112311736445352115976,2799753247995266147347843956,89333847417312523954034046936,2866354076060681446555954224696,92437346938232317505833680390576,2994900985489170290757597878329836,97447591912171214828256883106131656
b=7: 7,91,1645,36715,948157,27210169,844691407,27840317995,961404929485,34453695086041,1272342313935007,48163840784596201,1861408648486464175,73216844934326184595,2923876764438202454245,118311576139002457750315,4843019129955904050049645,200285107637533495666136425,8358772615816778247959630575,351714866444503250286117523465,14908928917141407766153378543855,636226231030579337275522117546915,27316683312186657375742935536430325,1179425650762021100313399983695203625,51184907683955175375444981664073129407
b=8: 8,120,2528,66424,2039808,70283424,2643158400,106391894904,4518833256512,200396211454720,9205443151733760,435368682010660000,21100379936684418560,1044115187294444772480,52597451834668445910528,2691037806733052553149304,139567074682665782246950080,7326035505602088571550898624,388692676307093660214513983232,20821560514105053341045830189824,1125071052804606862976953994944512,61270967628785336626069622639938560,3360729604535080838933867116477304832,185546031656019274031650908498763590816,10305688197366722917347399525269829719808
b=9: 9,153,3681,111321,3965409,159700401,7071121017,337386574809,17091486402849,909016606157553,50322658889918457,2880377856928886769,169569570177502573113,10224662030381995585353,629363959548197255942481,39439533486823544576759769,2510598661017963812643872673,162048024871184932602477539121,10589311049996692605851511195321,699665020890634054314125660348721,46691291797797628279076275449673209,3144108851688963610024995327698430153,213463410619979489585755003304528851281,14601839580495416551702129260310203023601,1005731803116618747973879291182068848700409
b=10: 10,190,5140,175870,7132060,329009500,16786131400,928136093950,54775566167500,3410496772665940,222005754890212600,15000987483726651100,1046188137708903907000,74962723424363171666200,5498130019391836779330640,411530535654245301470621950,31356088574298606320386653100,2427021041017043491153965921700,190503335542483372330410996741400,15141611551571136170888372040881620,1217139074924843939858814351399207400,98842200132473370747145638230967749800,8101728898006244345372661851453503553200,669726723998568836522230181105574128110300,55795274602503578824222289822443655978969560

Interestingly, some of these sequences do appear to exist in the OEIS! Specifically, as of the time of writing this blog post, these ones match the first terms generated:

b=2
A000984 Central binomial coefficients: \(\binom{2n}{n} = \frac{(2n)!}{(n!)^2}\)
b=3
A002893 \(a(n) = \sum_{k=0}^{n} \binom{n}{k}^2 \binom{2k}{k}\)
b=4
A002895 Domb numbers: number of 2n-step polygons on diamond lattice. \(a(n) = \sum_{k=0}^{n} \binom{n}{k}^2 \binom{2k}{k} \binom{2(n-k)}{n-k}\)
b=5
A169714 The function \(W_5(2n) = \int_0^1 \cdots \int_0^1 \left(\sum_{i=1}^{5} \cos(2\pi x_i)\right)^{2n} dx_1 \cdots dx_5\)
b=6
A169715 The function \(W_6(2n) = \int_0^1 \cdots \int_0^1 \left(\sum_{i=1}^{6} \cos(2\pi x_i)\right)^{2n} dx_1 \cdots dx_6\)
b=8
A385286 \(a(n) = (n!)^2 [x^n]\, {}_0F_0\!\left(\left[\,\right]; \left[1\right]; x\right)^8\)

Unfortunately, it’s beyond my current mathematical ability to prove that they’re equivalent, but there are a few things that I can mention.

For \(b=5\) and \(b=6\), Proposition 1 on page 6 of Borwein et al (2011)1 mentions the definition of \(W_n(2k)\)

\[W_n(2k) = \sum_{a_1+\cdots+a_n=k} \binom{k}{a_1,\ldots,a_n}^2\]

which on page 7, they mention is also the exact same formula as the definition of the combinatorial sums of multinomial coefficients squared

\[f_n(k) = \sum_{a_1+\cdots+a_n=k} \binom{k}{a_1,\ldots,a_n}^2\]

Those sums are discussed in Richmond and Shallit (2008)2 in which they mention that \(f_n(k)\) counts abelian squares — strings \(xx'\) of length \(2k\) over an alphabet with \(n\) letters such that \(x'\) is a permutation of \(x\) — and that \(f_2(n)\) matches sequence A000984, \(f_3(n)\) matches sequence A002893, \(f_4(n)\) matches sequence A002895.

Given that my TOTP problem matches with the definition of what \(f_{n}(k)\) would be in Richmond and Shallit for a string of length \(2k\) and an alphabet of \(n\) letters, we can then rewrite that the \(k\)th term of my sequence is \(a_{b}(n)=f_b(k)\). This allows us to conclude that:

b=2
\(a_{2}(n)=f_2(k)\), which Richmond and Shallit mention is A000984
b=3
\(a_{3}(n)=f_3(k)\), which Richmond and Shallit mention is A002893
b=4
\(a_{4}(n)=f_4(k)\), which Richmond and Shallit mention is A002895
b=5
\(a_{5}(n)=f_5(k)\), since \(f_5(k)=W_5(2k)\) by definition, this is A169714
b=6
\(a_{6}(n)=f_6(k)\), since \(f_6(k)=W_6(2k)\) by definition, this is A169715

This leaves us with the case of \(b=8\). This appears to match A385286, and in their definition, they mention that

We regard this sequence in the list of sequences n -> A287316(n, 2^k) for k = 3.

and they cross-reference

A000984 (k=1), A002895 (k=2), this sequence (k=3), A287316

This would suggest that the pattern is \(f_{2^k}\), so for A000984 \(f_2=f_{2^1}\), and for A002895 \(f_4=f_{2^2}\), meaning that A385286 is effectively \(f_{2^3}\) in disguise and thus the same as this sequence for \(b=8\).

Of note is the fact that \(b \in \{7,9\}\) aren’t in the OEIS, which is probably an interesting artifact of nobody ever needing them or studying them in a way that would lead them to add them to the OEIS, rather than them being completely novel. Given that Richmond and Shallit only cover \(f_n\) for \(n \leq 6\), and \(f_8\) is there due to \(2^k\) for \(k=3\), that would explain the lack of \(f_7\) and \(f_9\) in the OEIS.

Still, for a random shower thought about TOTP codes, it was quite an interesting mathematical rabbit hole to fall into.

Footnotes

  1. Borwein, J. M., Nuyens, D., Straub, A., & Wan, J. (2011). Some arithmetic properties of short random walk integrals. The Ramanujan Journal, 26(1), 109–132. doi:10.1007/s11139-011-9325-y↩︎

  2. Richmond, L. B., & Shallit, J. (2008). Counting Abelian squares. The Electronic Journal of Combinatorics, 16. arXiv:0807.5028↩︎