13
\$\begingroup\$

Write a program or function that accepts an integer in the range 1..3999 as input and returns the number of line segments required to express that integer in standard Roman numerals (so you would use XL but not VM). Examples:

   1 -> 1
   4 -> 3
   5 -> 2
   9 -> 3
  10 -> 2
  40 -> 4
  50 -> 2
  90 -> 3
 100 -> 1
 400 -> 3
 500 -> 2
 900 -> 5
1000 -> 4

Roman number conversion builtins are permitted, but you can solve the problem without them by repeatedly subtracting the largest remaining number from the above list. Example: 1234 = 4 + 1 + 1 + 2 + 2 + 2 + 3 = 15.

This is , so the shortest program wins.

\$\endgroup\$
3
  • \$\begingroup\$ Why is 10 two line segments instead of four? When writing X, you'd typically only write two lines, but doesn't the intersection of the lines make it four segments? \$\endgroup\$
    – Alex A.
    Commented Mar 17, 2016 at 20:02
  • \$\begingroup\$ @AlexA. The definition of line segment is usually something like: "The set of points following the shortest path between two points". There doesn't seem to be any reason to cut X based on this, you only need two sets of end points to define it. (Assuming Romans primarily wrote on euclidean geometries, I guess) \$\endgroup\$ Commented Mar 17, 2016 at 21:03
  • \$\begingroup\$ @FryAmTheEggman Hm okay. Good to know, thanks. \$\endgroup\$
    – Alex A.
    Commented Mar 17, 2016 at 21:05

9 Answers 9

5
\$\begingroup\$

C, 148 129 chars

d,x,n[]={1000,900,500,400,100,90,50,40,10,9,5,4,1,4,5,2,3,1,3,2,4,2,3,2,3,1};f(c){while(d+=(c/n[x])*n[x+13],c%=n[x++]);return d;}

My first code-golf :^). Since the question states I can use a function, I have changed main to a function to trim some chars (most importantly: pass c as parameter rather scanf)

unpacked

d,x,n[]={1000,900,500,400,100,90,50,40,10,9,5,4,1,4,5,2,3,1,3,2,4,2,3,2,3,1};
f(c){
  while(d+=(c/n[x])*n[x+13],
        c%=n[x++]);
  return d;
}
\$\endgroup\$
2
\$\begingroup\$

Pyth, 92 76 70 bytes

KsMc."/9hæ²z³Þ§ªW×Oû[Tnè,O¤"\/WQ=Q-Q=Nef!>TQ%2K aY@KhxKN;sY

Try it here!

Thanks to @FryAmTheEggman for some string packing suggestions which saved me some bytes!

I am still wondering if there is a mathematical way of encoding this list. Will try to figure something out.

Explanation

This uses the given algorithm. K contains the given list with the numbers and the corrosponding number of line segments in alternation. This list gets constructed by splitting a packed string, which gets decoded to 0/0/1/1/4/3/5/2/9/3/10/2/40/4/50/2/90/3/100/1/400/3/500/2/900/5/1000/4, on / and mapping each element to an integer.

KsMc."..."\/WQ=Q-Q=Nef!>TQ%2K aY@KhxKN;sY    # Q = input

   c."..."\/                                 # split the string on /
KsM                                          # map every number to int and assign to K
            WQ                               # while Q != 0
                     f    %2K                # only take every 2nd element of K and filter with T
                      !>TQ                   # T <= Q
                  =Ne                        # Take the last element of that and assign that to N
              =Q-Q                           # Q = Q - N
                                   xKN       # index of the first occurence of N in K
                                  h          # increment that index because we want the line segments
                              aA@K           # get the line segment from that index and append that to Y
                                      ;sY    # end the loop and print the sum of all line segments in Y
\$\endgroup\$
0
2
\$\begingroup\$

Mathematica, 80 72 bytes

Tr[Characters[#~IntegerString~"Roman"]/.{"I"|"C"->1,"M"->4,_String->2}]&

Anonymous function that just converts numbers to Roman numerals, replaces each character with its number of segments, and takes the total.

\$\endgroup\$
2
\$\begingroup\$

Retina, 128 bytes

.+
$*
1{1000}
t'
1{900}
td
1{500}
d
1{400}
t
1{100}
'
1{90}
t
1{50}
d
1{40}
t'
1{10}
d
1{9}
t
1{5}
d
1{4}
t
1
'
t
d'
d
''
'+
$.0

Simple replacing until there's nothing left to replace. Then the apostrophes are counted and that's our number of line segments.

If input and output in unary are allowed, it's 115 bytes (although who would want to type 1234 ones?).

Try it online!
Try it online! (unary IO)

\$\endgroup\$
2
\$\begingroup\$

Python 3, 95 bytes

def f(a,b=0):
 for e in'᝴ᔝ஺ॣəȟĮô>9 ':e=ord(e);d=e//6;b+=a//d*(e%6);a%=d
 return b

The Unicode string consists of the code points:

6004 5405 3002 2403 601 543 302 244 62 57 32 27 7
\$\endgroup\$
3
  • \$\begingroup\$ If you turn that string into a byte literal you can omit e=ord(e); \$\endgroup\$
    – xsot
    Commented Mar 20, 2016 at 3:31
  • \$\begingroup\$ I don’t think that works in my case. I need a Unicode string :( i.e. I’m looping over the codepoints in that string, not over the bytes. \$\endgroup\$
    – lynn
    Commented Mar 20, 2016 at 3:34
  • 1
    \$\begingroup\$ Oh, I see. Do you mind providing a hex dump of the string? It isn't displayed properly on my phone. \$\endgroup\$
    – xsot
    Commented Mar 20, 2016 at 3:40
2
\$\begingroup\$

JavaScript (ES6), 79 bytes

n=>"0123323453"[[,a,b,c,d]=1e4+n+'',d]-(-"0246424683"[c]-"0123323455"[b])+a*4

The strings represent the number of line segments for the units, tens and hundreds digits. (Thousands is simply four times the thousands digit.) This method seems to be shorter than other options such as the algorithm suggested in the question.

Edit: Saved 2 bytes thanks to @user81655.

\$\endgroup\$
2
  • \$\begingroup\$ This is a cool algorithm. Rearranging the casts can save 2 bytes as well: n=>"0123323453"[[,a,b,c,d]=1e4+n+'',d]-(-"0246424683"[c]-"0123323455"[b])+a*4 \$\endgroup\$
    – user81655
    Commented Apr 20, 2016 at 3:13
  • \$\begingroup\$ @user81655 Oh, that's nice: simply changing the +s to -s allows me to remove the leading +, but then the grouping saves another byte. \$\endgroup\$
    – Neil
    Commented Apr 20, 2016 at 7:29
1
\$\begingroup\$

Java, 152 bytes

Because, ya know, Java.

n->{int c=0;int[]r={999,4,899,5,499,2,399,3,99,1,89,3,49,2,39,4,9,2,8,3,4,2,3,3,0,1};for(int i=0;i<26;i+=2)while(n>r[i]){n-=r[i]+1;c+=r[i+1];}return c;}

Simple literal implementation of the given algorithm. Array packs the transform information: even indexes are one less than the roman numeral and odd indexes are the count for that numeral.

This is a lambda that takes and returns an int/Integer. This includes IntUnaryOperator or UnaryOperator<Integer>.

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

R, 62 bytes

\(x)sum(utf8ToInt(chartr("MXVDLIC","4222211",as.roman(x)))-48)

Attempt This Online!

This is code-golf, and here is the shortest answer (as for today).

A function which inputs a number and outputs the total number of segments.

This solution is based on the builtin function as.roman, the roman digits are replaced with the respective number of the lines and the total sum of lines is output.

The number with the most lines (30) is MMMDCCCLXXXVIII or 3888.

\$\endgroup\$
3
  • \$\begingroup\$ Newer golfing languages can probably do much better than what was popular eight years ago... \$\endgroup\$
    – Neil
    Commented Jun 8 at 7:12
  • \$\begingroup\$ @Neil I am not sure that esolangs can compete here with a builtin \$\endgroup\$ Commented Jun 8 at 7:49
  • \$\begingroup\$ Some of them have builtins too e.g. Vyxal has øṘ to convert to (or from) Roman Numerals. \$\endgroup\$
    – Neil
    Commented Jun 8 at 8:37
1
\$\begingroup\$

vemf, 30 bytes

éΦ╒¡(4↕;æ1╘*1214╛+╞405‼╕,335)+

Function that takes an integer.

The goal of the big parenthesis is to generate the table digit->segment. For every position, there are three symbols that we need to account for. We are going to try to generate the digits 0-8, and leave 9 until the end.

Notice the amount of "I"s:

   _ I II III IV V VI VII VIII
=> 0 1 2  3   1  0 1  2   3

which is 0 1 2 3, or 4↕, joined to itself ; with a one 1. This pattern follows with M, C, V, I with 4 1 2 1 segments respectively. We only need the first 4 elements for M, but we will keep them there.

    )9 4↕;æ1╘*1214
((0 1 2  3 1 0 1 2  3)
 (0 2 4  6 2 0 2 4  6)
 (0 1 2  3 1 0 1 2  3)
 (0 4 8 12 4 0 4 8 12))

Accounting for V, L, D is easy because all of them have 2 segments:

   _ I II III IV V VI VII VIII
=> 0 0 0  0   2  2 2  2   2

Easy! Just add 405‼. Or 45‼¼ or even doing it like ╒+├≥4¼ works. (That Each Trim should probably not be necessary, but whatever.)

For the 9s places I had a very nice solution at first where the next digit would add to the earlier list with some fill magic but it turns out that just adding the segment counts for IX, XC and CM manually is much easier. The null at the end is outside of the domain of the problem.

    )9 4↕;æ1╘*1214╛+╞405‼╕,335
((0 1 2  3 3 2 3  4  5 3)
 (0 2 4  6 4 2 4  6  8 3)
 (0 1 2  3 3 2 3  4  5 5)
 (0 4 8 12 6 2 6 10 14 ■))

The reason we found this table in reverse order is because éΦ conveniently returns the elements in little-endian order, so the digits are in the right place no matter how many digits the number has. this means that we can just each index to get the results (╕¡ happens to work as well because of fill, but ╒¡ does less work. also I kind of like that I managed to fit all the ╒╕╘╛ glyphs once). The + at the end sums the digits.

Try it online

\$\endgroup\$

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