13
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ The justification for why this isn't a duplicate clutters the spec. It would be better without it IMO. \$\endgroup\$ – Mego Apr 17 '16 at 5:51
  • \$\begingroup\$ Where would I add the justification? \$\endgroup\$ – Leaky Nun Apr 17 '16 at 5:51
  • 1
    \$\begingroup\$ Leave it out. If someone questions whether or not it is a duplicate, have the discussion in comments or in chat. \$\endgroup\$ – Mego Apr 17 '16 at 5:52
  • \$\begingroup\$ @Mego Done. :-) \$\endgroup\$ – Leaky Nun Apr 17 '16 at 5:53
  • \$\begingroup\$ Is (IV) an acceptable representation of 4000? \$\endgroup\$ – Neil Apr 17 '16 at 9:52
9
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ darn mathematica with its builtins for everything >:( \$\endgroup\$ – OldBunny2800 Apr 17 '16 at 18:47
  • 1
    \$\begingroup\$ @OldBunny2800 I'd be surprised if this wasn't beaten by any of the golfing languages anyway though. \$\endgroup\$ – Martin Ender Apr 17 '16 at 19:01
  • \$\begingroup\$ @OldBunny2800 And it costs real money to get it. That's bad. \$\endgroup\$ – Erik the Outgolfer Apr 18 '16 at 10:19
  • \$\begingroup\$ @MartinBüttner I thought simply RomanNumeral can do it? \$\endgroup\$ – Leaky Nun Apr 18 '16 at 12:15
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – Martin Ender Apr 18 '16 at 12:21
7
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

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"))
\$\endgroup\$
2
\$\begingroup\$

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.

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

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!

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

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]}"}
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – Value Ink Apr 19 '16 at 4:53
  • \$\begingroup\$ "IVXXLCCDM".scan(/(.)(.)(.)/){|x,b,c|... works too \$\endgroup\$ – Value Ink Jul 10 '17 at 21:38
  • \$\begingroup\$ Also r[x] is functionally equivalent to r.(x) whenever stabby lambdas are involved \$\endgroup\$ – Value Ink Jul 10 '17 at 21:40
  • \$\begingroup\$ @ValueInk thank you. That r[x] trick is going to be useful for all my futer recursive golf in ruby! \$\endgroup\$ – MegaTom Jul 10 '17 at 21:44
1
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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