6
$\begingroup$

I'm trying to write a function f.

example:

 f /@ Range[0, 52]

 (* ==> {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
   AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA} *)

f /@ (10^Range[8])

(* ==> {K,CW,ALM,NTQ,EQXE,BDWGO,UVXWK,HJUNYW} *)

How I do it?

$\endgroup$
2

3 Answers 3

11
$\begingroup$

A version using recursion:

g[n_ /; n < 26] := {n}
g[n_] := {g[Quotient[n, 26] - 1], Mod[n, 26]}
f[n_] := FromCharacterCode[65 + Flatten[g[n]]]

f /@ (10^Range[8])

{"K", "CW", "ALM", "NTQ", "EQXE", "BDWGO", "UVXWK", "HJUNYW"}

$\endgroup$
6
$\begingroup$

Three variants of the idea in @Simon's answer using FixedPoint, NestWhile and ReplaceRepeated:

ClearAll[chrctrDgtsF1,chrctrDgtsF2,chrctrDgtsF3];

FixedPoint:

chrctrDgtsF1 =  Function[{n}, FromCharacterCode[65 + 
  FixedPoint[If[First[#] < 26, #, 
     Join[QuotientRemainder[First@#, 26] + {-1, 0},  Rest[#]]] &, {n}]]];

NestWhile:

chrctrDgtsF2 = Function[{n}, FromCharacterCode[65 +
  NestWhile[Join[QuotientRemainder[First@#, 26] + {-1, 0}, Rest[#]] &, {n}, 
    (First[#] >= 26 &)]]];

ReplaceRepeated:

chrctrDgtsF3 = FromCharacterCode[65 + ({#} //.
   {x_, y___} /; x >= 26 :> Flatten@{QuotientRemainder[x, 26] + {-1, 0}, y})] &;

Examples:

chrctrDgtsF1 /@ (10^Range[8])
(* {"K","CW","ALM","NTQ","EQXE","BDWGO","UVXWK","HJUNYW"} *)

(f /@ # == chrctrDgtsF1 /@ # == chrctrDgtsF2 /@ # == chrctrDgtsF3 /@ #) &@
    RandomInteger[10^9, {1000}]  
(* True *)
$\endgroup$
6
$\begingroup$

Edit 1 Thanks to Simon and whuber for pointing out that the original post contained an error. While the approach that I described yields the correct results for the test range, it produces wrong results for other ranges. For example, for Range[670, 680] the results are {"YU", "YV", "YW", "YX", "YY", "YZ", "A@A", "A@B", "A@C", "A@D", "A@E"}, which is clearly not as required.

Edit 2 As whuber points out, the problem with this base is that depending on the circumstances either a 0 or a 1 may correspond with A. For example, when the result of IntegerDigits[n, 26] is {1,0} the string should be AA.

I have adapted the function in such a way that all 0's are done away with, and 1 is set to always mean A. First I increment the input by 1, so the result for 0 becomes {1}, which equals A. Then all 0's which are not at the starting position are replaced by 26 while the preceding number is decremented by one. Then all 0 at the start of the list are simply removed.

This means that the previous result of "A@A" ({1,0,1}) becomes "ZA" ({26, 1}).

 j[n_] := FromCharacterCode[64 + (IntegerDigits[n + 1, 26]
          //. {
                  {s___, p_, 0, r___} -> {s, p - 1, 26, r},
                  {0, r___} -> {r}
          })]

I have validated the results with the method of Simon up to 10^6.

Original post This uses IntegerDigits with base 26 and a correction for the last digit:

FromCharacterCode[64 + (IntegerDigits[#, 26] /. {m___, n_} -> {m, n + 1})] & /@ (10^Range[8])
(* {"K", "CW", "ALM", "NTQ", "EQXE", "BDWGO", "UVXWK", "HJUNYW"} *)
$\endgroup$
2
  • 3
    $\begingroup$ Nice code, but it gives A@A instead of ZA for an input value of 676 $\endgroup$ Commented Oct 25, 2012 at 20:06
  • 2
    $\begingroup$ This is a great idea, but implementing it is a little trickier than it may look: although this looks like a base 26 code, the correction for the last digit is not what you would expect. The roles of the digits 'a' through 'z' are different in the units place than elsewhere. In the units place, 'a' is a zero, whereas elsewhere, 'a' is a one and the role of zero is played by a blank. This makes it a combination of a base 26 and a base 27 code. $\endgroup$
    – whuber
    Commented Oct 25, 2012 at 20:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.