September 21, 2010

Raf's Problem

Another nice problem for testing your programming skills:

Given a String (x) containing only characters a-z, write a function (f)
that returns a base 10 integer, which converts the String as if it were
a base 26 numeral. Function f is bijective**.


Here are some example runs:


x      | f(x)
empty  | 0
a      | 1
b      | 2
z      | 26
aa     | 27
az     | 52
ba     | 53
bz     | 78
aaa    | 703
aaz    | 728
... 
aza    | 1353

Using your preferred programming language, implement function f.

* - it's known as Raf's problem and I saw it on Tony Morris `lambda blog
** - BUG: this function is injective since we can't use `spaces in number to string encoding ***
*** - we can make use of spaces if numeral separator is two-spaced but it is not what was asked - see example for encoding 26 and 27


Following is sample solutions done in Scala / Python / Haskell. Not sure about complexity level but it seems that python isn't shortest language anymore??

Haslell
str2num = foldl (\acc x -> acc * 26 + (ord x - (ord 'a') + 1)) 0

Scala
def str2num(str: String): Int = {
    val base = 26
    val offset = 'a'.toInt -1
    var res = 0
    for (c <- str) {
      val digit = if (c == ' ') 0 else (c.toInt - offset)
      res = res * base + digit
    }
    res
  }

  def num2str(n: Int): String = {
    val base = 26
    val offset = 'a'.toInt -1

    def aux(n: Int): List[Char] = (n / base, n % base) match {
      case (0, 0) => List(' ')
      case (0, r) => List((r + offset).toChar)
      case (q, 0) => 'z' :: aux(q-1)
      case (q, r) => (r + offset).toChar :: aux(q)
    }


    val res = aux(n).reverse.mkString
    if (res == " ") res else res.trim
  }

  test("Raf's Problem"){
    // x   | f(x)
    val tests = Map (
      " "   ->  0,
      "a"   ->  1,
      "b"   ->  2,
      "z"   ->  26,
      "aa"  ->  27,
      "az"  ->  52,
      "ba"  ->  53,
      "bz"  ->  78,
      "aaa" ->  703,
      "aaz" ->  728,
      "aza" ->  1353)
    for (t <- tests) {
      println(t._1 + " -> " + t._2 + " => <" + num2str(t._2) + ">")
      assert(str2num(t._1) == t._2)
      assert(t._1 == num2str(t._2))
    }
  }

Python
import string


def code(number):
    if number>0 and number<27:
        return chr(number+96)

def decode(s):
    if s in string.ascii_lowercase:
        return ord(s)-96


def coding(number):
    if number==0:
        return ""
    s=[]
    while number>0:
        x=number%26
        if x==0:
            x=26
            number=number-26
        y=code(x)    
        t=[y]+s
        s=t
        number=number/26

    return ''.join(s).split()[0]

def decoding(s):
    l=len(s)
    num=0
    for i in s:
        if not i==' ':
            c=decode(i)
            num=num*26+c
    return num



Super python
#!/usr/bin/python
 
from math import pow
from sys import argv

"""Details
16.09.2010
Given a String (x) containing only characters a-z, write a function (f) that returns a base 10 integer, which converts the String as if it were a base 26 numeral. Function f is bijective.

Nicu Marcu
*.*@*-*.com
"""

letters=map(chr, range(97,123))
 
def str2num(text):
 to_str2num=[]
 for l in text:
  index=1
  for ll in letters:
   if l == ll:
    to_str2num.append(index)
   index+=1
 index=0
 result=0
 for l in reversed(to_str2num):
  p = int(pow(26,index))
  result+=l*p
  index+=1
 
 return result
  

def num2str(nr):
 result=[]
 
 a = divmod(int(nr),26)
 rez = a[0]
 rest = a[1]
 
 if rez==0:
  result.append(rest)
  return result
 if rez==1 and rest==0:
  result.append(26)
  return result
 while(1):
  if rest==0:
   result.append(26)
   rez-=1
  else:
   result.append(rest)
  if rez==0:
   return result
  if rez<=26:
   result.append(rez)
   return result
   break
  else:
   a = divmod(rez,26)
   rez = a[0]
   rest = a[1]
 
def main():
 funct = argv[1]
 arg = argv[2]
 if funct=="str2num":
  print str2num(arg)
 if funct=="num2str":
  a = reversed(num2str(arg))
  world=""
  for i in a:
   world+=letters[i-1]
 
  print world
 return 1
main()

No comments:

Post a Comment