11
\$\begingroup\$

The usual rules: no external resources, or the like. Input will be a positive integer in valid Roman numeral format less than 10 thousand, say.

Use all the Roman numeral characters:

  • I = 1
  • V = 5
  • X = 10
  • L = 50
  • C = 100
  • D = 500
  • M = 1,000

Respect subtractive notation (copied from the Wikipedia page):

  • the numeral I can be placed before V and X to make 4 units (IV) and 9 units (IX) respectively
  • X can be placed before L and C to make 40 (XL) and 90 (XC) respectively
  • C can be placed before D and M to make 400 (CD) and 900 (CM) according to the same pattern[5]
\$\endgroup\$
2
  • \$\begingroup\$ How should we represent ten thousand? MMMMMMMMMM or something else? \$\endgroup\$
    – ProgramFOX
    Dec 27, 2013 at 15:34
  • \$\begingroup\$ @ProgramFOX That sounds right. I've changed the spec to under 10,000. \$\endgroup\$
    – Ben Reich
    Dec 27, 2013 at 15:37

8 Answers 8

8
\$\begingroup\$

Mathematica, 33 chars

FromDigits[InputString[],"Roman"]
\$\endgroup\$
1
  • \$\begingroup\$ I'd say "FromDigits" violates "no external resources, or the like". \$\endgroup\$ Dec 28, 2013 at 1:53
6
\$\begingroup\$

JavaScript, 98 91 characters:

for(k=d=l=0,a=prompt();i={I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}[a[k++]];l=i)d+=i>l?i-2*l:i;d

Expanded form:

a = prompt(), // a is input string
d = 0,        // d is result sum
l = 0,        // l is last value processed (for comparison with current val)
k = 0,        // k is current position in string

v = {I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}; // map of values

// for each char in string a[k] (left to right), put its base-10 value in i
while(i = v[a[k++]]) {

    // if previous left-side value is lower than this value,
    //     it should have been substracted, so subject twice its value
    //     (once to negate the past addition, and again to apply negative value)
    // regardless, add this value
    if(i>l) {
        d += i - 2*l;
    } else {
        d += i;
    }

    // store this value as previous value
    l=i;
}

// yield result
d;
\$\endgroup\$
3
  • \$\begingroup\$ You can save some characters by removing l=i and replacing d+=i>l?i-2*l:i with d+=i>l?i-2*l:l=i \$\endgroup\$
    – ProgramFOX
    Dec 27, 2013 at 16:37
  • \$\begingroup\$ @ProgramFOX This doesn't quite seem to work, because l doesn't get reassigned in the true case. (For example: IX is rendered as 11.) This may have worked in my previous right-to-left code, but I think this doesn't apply anymore. \$\endgroup\$
    – apsillers
    Dec 27, 2013 at 16:50
  • \$\begingroup\$ Oh yes, that's true. You also need to replace your > condition with >= if you want to remove the l=i condition. I don't know whether it still saves characters then, though. \$\endgroup\$
    – ProgramFOX
    Dec 27, 2013 at 16:52
3
\$\begingroup\$

Scala 195

val t=Map('I'->1,'V'->5,'X'->10,'L'->50,'C'->100,'D'->500,'M'->1000)
def f(n:String)={val r=n.substring(1).foldLeft((0,t(n.head)))((a,x)=>(if(a._2>=t(x))a._1+a._2 else a._1-a._2,t(x)));r._1+r._2}
\$\endgroup\$
3
\$\begingroup\$

GolfScript: 83

0\{[77 68 67 76 88 86 73]?[1000 500 100 50 10 5 1]=}%{)@.@.@<{~)}{}if@.}do;{+\.}do;
\$\endgroup\$
2
  • \$\begingroup\$ If only we could make the mapping shorter... \$\endgroup\$
    – Dan G
    Dec 27, 2013 at 21:57
  • \$\begingroup\$ Props to @DanG for the help \$\endgroup\$
    – Ben Reich
    Dec 27, 2013 at 21:57
3
\$\begingroup\$

MS Excel,12

Enter a value in A1, then enter this in A2

=ARABIC(A1)
\$\endgroup\$
1
  • \$\begingroup\$ If you change the language to Google Sheets you can drop the terminal ) for -1 Byte \$\endgroup\$ Nov 5, 2017 at 15:07
2
\$\begingroup\$

Python 201 characters

m={'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900,'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
f=lambda s:m[s] if s in m else(m[s[:2]]+f(s[2:])if s[:2]in m else m[s[0]]+f(s[1:]))
f(raw_input())

Example:

>>> m={'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900,'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000};f=lambda s:m[s] if s in m else(m[s[:2]]+f(s[2:])if s[:2]in m else m[s[0]]+f(s[1:]));f(raw_input())
MMMCMLXXXVII
3987

119 117 characters

>>> X=[dict(zip('MDCLXVI',(1e3,500,100,50,10,5,1)))[x]for x in raw_input()];sum((x,-x)[x<y]for x,y in zip(X,X[1:]))+X[-1]
MMMCMLXXXVII
3987.0

Expanded form:

>>> # Maps roman numbers to their decimal counterpart
>>> roman_map = dict(zip('MDCLXVI',(1e3,500,100,50,10,5,1)))
>>> # Maps the list of input number to the corresponding decimals
>>> X=[roman_map[x]for x in raw_input()]
>>> # A number will be added (x) or subtracted (-x) depending on its value vs the next number
>>> # For instance CM -> [100, 1000], 100 < 1000, therefore -100, +1000
>>> result = sum((x,-x)[x<y] for x,y in zip(X,X[1:])) # True = 1, False = 0
>>> # Adding the last element (which is always added)
>>> result += X[-1]
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I dig the 1e3 one-char savings but the question demands "integer output". \$\endgroup\$ Dec 27, 2013 at 19:01
1
\$\begingroup\$

JavaScript, 198 characters

n={I:1,V:5,X:10,L:50,C:100,D:500,M:1e3,IV:4,IX:9,XL:40,XC:90,CD:400,CM:900},s=prompt(),a=i=0;for(;i<s.length;i++){b=(j)=>s.substr(j,1);c=b(i);d=b(i+1);e=n[c+d];if(e){a+=e;i++}else{a+=n[c]}}alert(a);

The ungolfed code:

n = {
    I: 1,
    V: 5,
    X: 10,
    L: 50,
    C: 100,
    D: 500,
    M: 1e3,
    IV: 4,
    IX: 9,
    XL: 40,
    XC: 90,
    CD: 400,
    CM: 900
}, s = prompt(), a = i = 0;
for (; i < s.length; i++) {
    b = (j) => s.substr(j, 1);
    c = b(i);
    d = b(i + 1);
    e = n[c + d];
    if (e) {
        a += e;
        i++
    } else {
        a += n[c]
    }
}
alert(a);

How this works: first, you store all values inside an object. Then, you prompt for input and you loop over all characters in the string. If the current character + the next character are a number (CM for example), then look up current char + next char in the object. If current char + next char isn't available in the object, just look up the current char.

Demo: http://jsfiddle.net/vSLfM/

\$\endgroup\$
1
\$\begingroup\$

Ruby, 134

i=gets
%w[IV,4 IX,9 XL,40 XC,90 CD,400 CM,900 I,1 V,5 X,10 L,50
C,100 D,500 M,1000].map{|s|k,v=s.split ?,;i.gsub!(k,?++v)}
puts eval i
\$\endgroup\$
1
  • \$\begingroup\$ Got this down to 119 characters these days: p`dd`.scan(/IV|I|XL|XC|CD|CM|./).sum{|i|i.chars.sum{[1,5,10,50,100,500,1000]["IVXLCDM".index _1].*i[1]&&_1==i[0]?-1:1}} \$\endgroup\$
    – DrQuarius
    Nov 2, 2023 at 3:58

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