I wanted a nice way to convert natural numbers to alphabetic representations like:
1->"A",
2->"B",
...
26->"Z",
27->"AA,
...
26*2->"AZ",
26*2+1->"BA",
...
And I knew this was mostly a conversion base 26 problem, except with the complication of lacking a 0
So things like:
In[412]:= IntegerDigits[26, 26]
Out[412]= {1, 0}
Would cause difficulties.
I solved this by unspooling via ReplaceRepeated where the sequence pattern {n_,0} maps to {n-1,26}, because, by the way digit representations are defined, {n,0} is equivalent to {n-1,0} and by having this cascade up / down the chain we get the right answer.
Here's the actual code for this:
intToAlpha[i_Integer?Positive,
alphabet : {__String} | Automatic : Automatic] :=
With[{alpha = Replace[alphabet, Automatic :> Alphabet[]]},
ToUpperCase@StringJoin@
Part[alpha,
DeleteCases[0]@
ReplaceRepeated[IntegerDigits[i, Length@alpha],
{s___, n_?Positive, 0, e___} :>
{s, n - 1, Length@alpha, e}
]
]
];
intToAlpha[i : {__Integer?Positive},
alphabet : {__String} | Automatic : Automatic] :=
With[{a = Replace[alphabet, Automatic :> Alphabet[]]},
intToAlpha[#, a] & /@ i
];
And this is decent all told:
In[433]:= AssociationMap[
intToAlpha,
RandomInteger[{1, 26*26*2}, 100]
]
Out[433]= <|1112 -> "APT", 907 -> "AHW", 870 -> "AGL", 938 -> "AJB",
1256 -> "AVH", 991 -> "ALC", 25 -> "Y", 203 -> "GU", 433 -> "PQ",
994 -> "ALF", 480 -> "RL", 762 -> "ACH", 576 -> "VD", 570 -> "UX",
931 -> "AIU", 1090 -> "AOX", 1237 -> "AUO", 404 -> "ON", 695 -> "ZS",
1180 -> "ASJ", 580 -> "VH", 1040 -> "AMZ", 198 -> "GP", 218 -> "HJ",
964 -> "AKB", 667 -> "YQ", 1135 -> "AQQ", 1285 -> "AWK",
763 -> "ACI", 825 -> "AES", 588 -> "VP", 841 -> "AFI", 1036 -> "AMV",
1268 -> "AVT", 592 -> "VT", 742 -> "ABN", 118 -> "DN", 599 -> "WA",
795 -> "ADO", 119 -> "DO", 640 -> "XP", 809 -> "AEC", 213 -> "HE",
289 -> "KC", 1293 -> "AWS", 51 -> "AY", 829 -> "AEW", 37 -> "AK",
491 -> "RW", 1340 -> "AYN", 521 -> "TA", 55 -> "BC", 895 -> "AHK",
1211 -> "ATO", 1130 -> "AQL", 498 -> "SD", 1038 -> "AMX",
753 -> "ABY", 1191 -> "ASU", 542 -> "TV", 92 -> "CN", 168 -> "FL",
949 -> "AJM", 317 -> "LE", 354 -> "MP", 1141 -> "AQW", 1310 -> "AXJ",
857 -> "AFY", 904 -> "AHT", 645 -> "XU", 1065 -> "ANY", 324 -> "LL",
684 -> "ZH", 903 -> "AHS", 679 -> "ZC", 90 -> "CL", 1101 -> "API",
427 -> "PK", 844 -> "AFL", 162 -> "FF", 159 -> "FC", 559 -> "UM",
398 -> "OH", 860 -> "AGB", 1216 -> "ATT", 871 -> "AGM", 671 -> "YU",
285 -> "JY", 389 -> "NY", 499 -> "SE", 889 -> "AHE", 67 -> "BO",
448 -> "QF", 211 -> "HC", 836 -> "AFD", 808 -> "AEB"|>
In[434]:=
intToAlpha[Range[100000]] // RepeatedTiming // First[#]/100000 &
Out[434]= 0.0000170
But I feel like there should be a more elegant (and likely faster) way, no?
Is there a way to do this directly, rather than converting via IntegerDigits then converting out the 0s? Is there some built-in function I'm missing?
Alphabet[]it doesn't seem to handle the transition from 1 digit to 2 properly (and there are some oddities further up in the 2-digit sequences). E.g.{intToAlphabet[{26, 27}]}gives{{"Z", "CA"}}and{intToAlphabet[{702}], intToAlphabet[{703}]}gives{{"BZ"}, {"CAA"}}. – b3m2a1 Jun 16 '17 at 14:42