Updated
For a long time I thought that Roman numerals were the worst counting system out there, until I got down to thinking about Microsoft Excel’s column naming convention. The last time I thought about Excel I wasn’t satisfied with the solution I had – the last time was at Applibase with Sid.
So what do I mean? You know the way columns are numbered in excel they go A, B, C.. till Z and then become AA, AB, AC … AZ, BA, BB, BC … BZ, CA, CB… etc. You get the idea I guess. So in some sense its fair to imagine that you can think of these as representing numbers.
So can you write a mapping from natural numbers to excel-numbers and vice versa?
Lets look at look at the problem in a reduced sense and see what we get – lets consider only A, B and C. Therefore we could count as follows – A, B, C, AA, AB, AC, BA, BB, BC, CA, CB, CC, AAA, AAB, AAC etc.
Lets write these down with their natural numbers next to them –
1 : A
2 : B
3 : C
4 : AA
5 : AB
6 : AC
7 : BA
8 : BB
9 : BC
10 : CA
11 : CB
12 : CC
13 : AAA
14 : AAB
Can you write a function that takes a number and returns the corresponding excel number? Its not as easy as you think. The reverse, ie, taking an excel number and producing a natural number out of it is easier, conceptually.
If you think about it enough you will realize that unlike many number systems out there this one is different because it does not have a zero character – hence you can’t express things like A00.
I don’t think I have a good enough solution yet. My current solution still checks if a value is 0 and if so does something otherwise does something else. I would like to write a solution in a clearly recursive fashion such that the only condition-check I do is to terminate the recursion. Think about how you convert a number from natural numbers to a base 2 representation for example – you can write this recursively and you only need a check to terminate your recursion. Here I need checks for other things. I guess in some sense what I am complaining about is that my number to excel-number conversion is not “mathematical” enough.
Anyway, here is solution as a piece of Haskell code – it’s a spoiler so don’t look at it until you have tried to solve it yourself. (Set base to 26 to get full A to Z numbering).
base = 3
ch :: Integer -> Char
ch n = intToDigit ((digitToInt 'a') + (fromInteger n) - 1)
num :: Char -> Integer
num c = toInteger $ (digitToInt c) - (digitToInt 'a') + 1
mdiv m n | m `mod`n == 0 = m `div` n - 1
mdiv m n = m `div` n
mmod m n | m `mod` n == 0 = n
mmod m n = m `mod` n
excel2int :: String -> Integer
excel2int s = excel2int' s 0
where
excel2int' (c:[]) r = r * base + (num c)
excel2int' (c:cs) r = excel2int' cs (r * base + (num c))
int2excel :: Integer -> String
int2excel n | n <= base = [(ch n)]
int2excel n = (int2excel (mdiv n base)) ++ [ch (mmod n base)]
Update
Here is a very clean solution, courtesy of Abhijit Mahabal who pointed it out in a couple of minutes. He even stated a proof and went on to show how you can do this with logarithms…
i2e n | n < 3 = [ch (n+1)]
i2e n = i2e (n `div` 3 - 1) ++ [ch ((n `mod` 3) + 1)]
The following solution (that I disagree with in spirit) is by Kyle Ross. It looks rather cute for some values of cute –
i2e = (gen !!) . pred
xs <+> ys = [x ++ y | x <- xs, y <- ys]
gen = base ++ (gen <+> base)
where base = ["a", "b", "c"]
Powered by: newtelligence dasBlog 2.0.7226.0
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2010, Roshan James
E-mail