Convert to Roman Numeral!

13

0

Your task is to convert a given positive integer from Arabic numeral to Roman numeral.

Things get difficult when you count to 4000.

The romans did this by adding a line above a symbol to multiply that symbol by 1 000. However, overlines aren't exactly displayable in ASCII. Also, there are double overlines to multiply a symbol by 1 000 000, and then triple overline to multiply a symbol by 1 000 000 000, etc...

Therefore, I decided to use parentheses to replace overlines.

The symbols can be individually placed in parentheses. For example, both (VI) and (V)(I) are valid representations of 6 000. (V)M is also a valid representation of 6000.

(I) is a valid way to represent 1 000.

Testcases

Input: 1
Output: I
Input: 2
Output: II
Input: 3
Output: III
Input: 4
Output: IV
Input: 15
Output: XV
Input: 40
Output: XL
Input: 60
Output: LX
Input: 67
Output: LXVII
Input: 400
Output: CD
Input: 666
Output: DCLXVI
Input: 3000
Output: MMM
Input: 3999
Output: MMMCMXCIX
Input: 4000
Output: M(V)
Input: 4999
Output: M(V)CMXCIX
Input: 6000
Output: (VI)
Input: 6000000
Output: ((VI))
Input: 6006000
Output: ((VI)VI)
Input: 6666666666
Output: (((VI)DCLXVI)DCLXVI)DCLXVI

Scoring

This is . Shortest code in bytes win.

Leaky Nun

Posted 2016-04-17T05:43:37.280

Reputation: 45 011

1The justification for why this isn't a duplicate clutters the spec. It would be better without it IMO. – Mego – 2016-04-17T05:51:04.630

Where would I add the justification? – Leaky Nun – 2016-04-17T05:51:59.077

1Leave it out. If someone questions whether or not it is a duplicate, have the discussion in comments or in chat. – Mego – 2016-04-17T05:52:35.700

@Mego Done. :-) – Leaky Nun – 2016-04-17T05:53:03.847

Is (IV) an acceptable representation of 4000? – Neil – 2016-04-17T09:52:02.090

Yes, clarified. – Leaky Nun – 2016-04-17T10:07:05.743

@Neil For that case, you can output which one you want, 'cause (IV) and MMMM are the same length in bytes. – Erik the Outgolfer – 2016-04-18T10:16:48.323

@ErikKonstantopoulos Not really. MMMM is not accepted. – Leaky Nun – 2016-04-18T12:41:42.223

Possible duplicate of Roman numeral converter function

– mbomb007 – 2016-04-19T19:22:55.210

@mbomb007 That's the opposite direction. – Martin Ender – 2016-04-19T19:46:00.370

Answers

9

Mathematica, 67 bytes

Fold["("<>#<>")"<>#2&,RomanNumeral[#~IntegerDigits~1000]/."N"->""]&

Avoids all trouble with M by converting the input to base 1000 and converting each digit separately with RomanNumeral. Then we fold them up by inserting (...) from the left.

Unfortunately, Mathematica represents zeros as N so we need to get rid of those.

Martin Ender

Posted 2016-04-17T05:43:37.280

Reputation: 184 808

1darn mathematica with its builtins for everything >:( – OldBunny2800 – 2016-04-17T18:47:48.600

1@OldBunny2800 I'd be surprised if this wasn't beaten by any of the golfing languages anyway though. – Martin Ender – 2016-04-17T19:01:18.737

@OldBunny2800 And it costs real money to get it. That's bad. – Erik the Outgolfer – 2016-04-18T10:19:47.603

@MartinBüttner I thought simply RomanNumeral can do it? – Leaky Nun – 2016-04-18T12:15:45.070

1@KennyLau It outputs MMMM for 4000, it only starts working to spec at 5000 (and then you get the same problem for 4000000 etc.). Even then, it uses overbars instead of parentheses. If you're fine with that, you should say so in the challenge specification. – Martin Ender – 2016-04-18T12:21:53.993

7

JavaScript (ES6), 136 bytes

f=n=>n<4e3?"M1000CM900D500CD400C100XC90L50XL40X10IX9V5IV4I1".replace(/(\D+)(\d+)/g,(_,r,d)=>r.repeat(n/d,n%=d)):`(${f(n/1e3)})`+f(n%1e3)

For numbers under 4000, repeats each Roman "letter" as many times as possible, using the list of Roman "letters" and their decimal values. Otherwise recursively builds up the answer from the division and modulo with 1000. Fortunately repeat truncates so I don't have to do it myself.

Neil

Posted 2016-04-17T05:43:37.280

Reputation: 95 035

3

Common Lisp, 108

(defun p(n)(if(> n 0)(if(< n 4000)(format()"~@R"n)(format()"(~A)~@[~A~]"(p(floor n 1000))(p(mod n 1000))))))

Ungolfed

(defun p(n)
  (if (> n 0)
      (if (< n 4000)

          ;; Built-in Roman formatter (between 1 and 3999)
          (format () "~@R" n)

          ;; Divide N by 1000, as 1000*Q + R.
          ;; First print (p Q) in parentheses (recursively)
          ;; Then, if it is not NIL, the remainder R.
          (format () "(~A)~@[~A~]"
                  (p (floor n 1000))
                  (p (mod n 1000))))))

Tests

Two tests give different outputs than the ones from the question:

(loop for (in out) in '((1 "I")
                        (2 "II")
                        (3 "III")
                        (4 "IV")
                        (15 "XV")
                        (40 "XL")
                        (60 "LX")
                        (67 "LXVII")
                        (400 "CD")
                        (666 "DCLXVI")
                        (3000 "MMM")
                        (3999 "MMMCMXCIX")
                        (4000 "M(V)")
                        (4999 "M(V)CMXCIX")
                        (6000 "(VI)")
                        (6000000 "((VI))")
                        (6006000 "((VI)VI)")
                        (6666666666 "(((VI)DCLXVI)DCLXVI)DCLXVI"))
   for computed = (p in)
   unless (string= out computed)
   collect (list in out computed))

=> ((4000 "M(V)" "(IV)")
    (4999 "M(V)CMXCIX" "(IV)CMXCIX"))

coredump

Posted 2016-04-17T05:43:37.280

Reputation: 6 292

2

R, 134

m=1000;x=scan();while(x[1]>=m)x=c(floor(x[1]/m),x[1]%%m,x[-1]);cat(rep("(",length(x)),sep="");cat(as.character(as.roman(x)),sep=")")

It's pretty much not the best option, but I think the idea should go pretty similar to this.

Masclins

Posted 2016-04-17T05:43:37.280

Reputation: 914

1

TCL 134 bytes

proc f r {
set map {M 1000+ CM 900+ D 500+ CD 400+ C 100+ XC 90+ L 50+ XL 40+ X 10+ IX 9+ V 5+ IV 4+ I 1+}
expr [string map $map $r]0}

Try it here: https://rextester.com/BJC92885

chau giang

Posted 2016-04-17T05:43:37.280

Reputation: 725

1

Ruby, 137 134 130 bytes

Recursive function that returns the string. I'm trying to golf down the numeral encodings a bit more if possible, but I'm not sure how.

Whoops, it's practically a direct port of @Neil's ES6 answer now.

f=->x{(x<t=1e3)?"CM900D500CD400C100XC90L50XL40X10IX9V5IV4I1".gsub(/(\D+)(\d+)/){v=$2.to_i;s=x/v;x%=v;$1*s}:"(#{f[x/t]})#{f[x%t]}"}

Value Ink

Posted 2016-04-17T05:43:37.280

Reputation: 10 608

1

Ruby, 185 161 144 bytes

r=->i{i>(t=1e3)? "(#{r[i/t]})"+r[i%t]:(s=?I*i;"IVXXLCCDM".scan(/(.)(.)(.)/){|x,y,z|{x*5=>y,x*4=>x+y,y*2=>z,y+x+y=>x+z}.map{|x,y|s.gsub!x,y}};s)}

Over a year after the original post, I think I learned something about golfing.

Thank you Value Ink for your valuable comments.

MegaTom

Posted 2016-04-17T05:43:37.280

Reputation: 3 787

gsub can take a string as the first argument, removing the need for substitutions into a regex pattern since s.gsub! x,y does it automatically. Other than that, you can probably just forgo the assignment of your a array since you only use it once, and put it directly into the each_slice call. – Value Ink – 2016-04-19T04:53:43.567

"IVXXLCCDM".scan(/(.)(.)(.)/){|x,b,c|... works too – Value Ink – 2017-07-10T21:38:33.513

Also r[x] is functionally equivalent to r.(x) whenever stabby lambdas are involved – Value Ink – 2017-07-10T21:40:22.370

@ValueInk thank you. That r[x] trick is going to be useful for all my futer recursive golf in ruby! – MegaTom – 2017-07-10T21:44:30.567

1

Python, 188 194

-6 bytes from getting rid of some whitespace

This challenge brought me back to when I was first learning to program...

def f(x,s=zip("M CM D CD C XC L XL X IX V IV I".split(),[1e3,900,500,400,100,90,50,40,10,9,5,4,1])):
 r=""if x<4e3else"("+f(x/1e3)+")";x%=1e3
 for a,b in s:
    while x>=b:r+=a;x-=b
 return r

It might not be the shortest solution, but I had fun golfing this problem.

Try it out!

Mr Public

Posted 2016-04-17T05:43:37.280

Reputation: 669