Those Greedy Romans!

30

1

Given a strictly positive integer, return the shortest possible Roman numeral using only the additive rule. Output must consist of zero or more of each of the characters MDCLXVI in that order. The number 14 must therefore give XIIII rather than XIV.

The characters' numeric values are M=1000, D=500, C=100, L=50, X=10, V=5, I=1.

Examples

3III

4 → IIII

9VIIII

42XXXXII

796DCCLXXXXVI

2017MMXVII

16807MMMMMMMMMMMMMMMMDCCCVII

Adám

Posted 2017-06-27T13:08:38.403

Reputation: 37 779

1You're a benevolent questioner to allow 4 -> IIII is 9 -> VIIII as well instead of IX? – Magic Octopus Urn – 2017-06-27T14:20:45.747

1Related: Convert to Roman Numeral – Kevin Cruijssen – 2017-06-27T14:21:03.403

@MagicOctopusUrn VIIII is the only allowed output for 9. – Adám – 2017-06-27T14:21:46.710

@Adám was just pointing out that you may want to add that as an example too because the rule for 4 and 9 are the same. – Magic Octopus Urn – 2017-06-27T14:34:32.883

@MagicOctopusUrn Added. – Adám – 2017-06-27T14:45:26.913

1Related – Arnauld – 2017-06-27T14:51:18.277

Answers

12

Plain English, 1059 1025 678 641 451 399 bytes

Saved 34 bytes by removing an error trap. Then saved 384 bytes by golfing. Then saved 190 bytes by combining the division operation with the append operation ("z") into a new operation ("p"). Then saved 52 bytes by golfing.

A s is a string.
To p a r remainder a s a x string a n number:
If the x is "", exit.
Divide the r by the n giving a q quotient and the r.
Fill a t s with the x's first's target given the q.
Append the t to the s.
To convert a r number to a s:
p the r the s "M" 1000.
p the r the s "D" 500.
p the r the s "C" 100.
p the r the s "L" 50.
p the r the s "X" 10.
p the r the s "V" 5.
p the r the s "I" 1.

Here is the ungolfed version of the final code, plus an error trap for a negative number:

A roman numeral is a string.

To process a remainder given a roman numeral and a letter string is a number:
  If the letter is "", exit.
  Divide the remainder by the number giving a quotient and the remainder.
  Fill a temp string with the letter's first's target given the quotient.
  Append the temp string to the roman numeral.

To convert a number to a roman numeral:
  If the number is negative, exit.
  Put the number in a remainder.
  Process the remainder given the roman numeral and "M" is 1000.
  Process the remainder given the roman numeral and "D" is  500.
  Process the remainder given the roman numeral and "C" is  100.
  Process the remainder given the roman numeral and "L" is   50.
  Process the remainder given the roman numeral and "X" is   10.
  Process the remainder given the roman numeral and "V" is    5.
  Process the remainder given the roman numeral and "I" is    1.

Jasper

Posted 2017-06-27T13:08:38.403

Reputation: 409

10Wait, is this a programming language? – Adám – 2017-06-27T19:47:32.117

3

@Adam -- Yes. Plain English compiles, and runs, and everything. The source code and IDE are available at https://github.com/Folds/english

– Jasper – 2017-06-27T20:16:28.803

1Do golf it though - after all, this is code golf, not a language showcase. – Sanchises – 2017-06-27T20:25:03.850

2So this is the language you use if you don't want your job outsourced I take it? – corsiKa – 2017-06-28T16:58:46.667

@corsiKa -- LOL! Only if enough of us start using it (and adding to its libraries) that it achieves critical mass. – Jasper – 2017-06-28T17:03:15.427

@corsiKa -- Lots of people know English (as a Second Language) well enough to use Plain English. And the language is designed to be customized to the user's dialect or native language. There is a professor in Argentina who has thought about using similar design principles to start a Plain Spanish language. Perhaps Español Llano? – Jasper – 2017-06-28T17:23:36.920

5

APL (Dyalog), 25 22 bytes

'MDCLXVI'/⍨(0,6⍴2 5)∘⊤

Try it online!

TwiNight

Posted 2017-06-27T13:08:38.403

Reputation: 4 187

Nice, and basically the solution I had in mind. However, you can use replicate (/) instead of reshape () so you can cut out the each and the catenate-reduction (¨ and ,/). – Adám – 2017-06-27T13:30:32.540

Also, you can convert to tradfn body and take input () and use commute () to remove the parens and compose (). – Adám – 2017-06-27T13:31:44.073

Thanks, but what do you mean by your second comment? I can't think of a way to do that without increasing byte count – TwiNight – 2017-06-27T14:04:04.783

'MDCLXVI'/⍨⎕⊤⍨0,6⍴2 5 – Adám – 2017-06-27T14:05:10.217

@Adám Unless we're talking about a REPL environment I don't think that matches any accepted output format – TwiNight – 2017-06-27T14:14:14.297

1Try it online! – Adám – 2017-06-27T14:16:04.917

1

That would be a snippet unless you count the {} or ∇f∇ surrounding the function

– TwiNight – 2017-06-27T14:25:14.027

No, plenty of answers contain just a tradfn body. The additional characters are just to name it. By "snippets" I mean just a piece of code which expects the input to be stored in a hardcoded variable and stores the output in another hardcoded variable, or prints it. Here you prompt for input. To name a function you do ⎕FX name body. The name doesn't add any functionality as long as it isn't a whole function header.

– Adám – 2017-06-27T14:32:16.800

5

Retina, 57 42 bytes

Converts to unary, then greedily replaces bunches of Is with the higher denominations in order.

.*
$*I
I{5}
V
VV
X
X{5}
L
LL
C
C{5}
D
DD
M

Try it online

Saved 15 bytes thanks to Martin

mbomb007

Posted 2017-06-27T13:08:38.403

Reputation: 21 944

That's very clever. – Adám – 2017-06-27T13:51:13.510

7

It's a lot shorter to go the other way: https://tio.run/##K0otycxL/K@q4Z7wX0@LS0XLk8uz2rSWK4wrLIwrgisCxPbh8vHhcuZyBrFduFxcuHz//zfmMuEyMeIytzTjMjIwNOcyNLMwMAcA

– Martin Ender – 2017-06-27T13:54:06.847

Couldn't you take input in unary using I as unit? – Adám – 2017-06-27T14:09:34.790

2@Adám Considering that Retina can now easily handle integer input, I think it's kind of cheap to do that. – mbomb007 – 2017-06-27T14:52:11.493

5

Python 2, 64 bytes

f=lambda n,d=5,r='IVXLCD':r and f(n/d,7-d,r[1:])+n%d*r[0]or'M'*n

Try it online!

Rather than creating the output string from the start by greedily taking the largest part, this creates it from the end. For instance, the number of I's is n%5, then the number of V's is n/5%2, and so on. This is mixed base conversion with successive ratios of 5 and 2 alternating.

Here's an iterative equivalent:

Python 2, 68 bytes

n=input();s='';d=5
for c in'IVXLCD':s=n%d*c+s;n/=d;d^=7
print'M'*n+s

Try it online!

The M's need to be handled separately because any number of them could be present as there's no larger digit. So, after the other place values have been assigned, the value remaining is converted to M's.

For comparison, a greedy strategy (69 bytes):

Python 2, 69 bytes

f=lambda n,d=1000,r='MDCLXVI':r and n/d*r[0]+f(n%d,d/(d%3*3-1),r[1:])

Try it online!

The current digit value d is divided by either 2 or 5 to produce the next digit. The value of d%3 tell us which one: if d%3==1, divide by 2; and if d%3==2, divide by 5.

xnor

Posted 2017-06-27T13:08:38.403

Reputation: 115 687

4

Mathematica, 81 bytes

Table@@@Thread@{r=Characters@"MDCLXVI",#~NumberDecompose~FromRomanNumeral@r}<>""&

Explicitly using the values and deriving the corresponding numerals seems to be one byte longer:

Table@@@Thread@{RomanNumeral[n={1000,500,100,50,10,5,1}],#~NumberDecompose~n}<>""&

Martin Ender

Posted 2017-06-27T13:08:38.403

Reputation: 184 808

1Nice!: FromRomanNumeral@r – DavidC – 2017-06-27T13:45:55.953

4

Excel, 236 193 161 bytes

43 bytes saved thanks to @BradC

At this point, the answer really belongs totally to @BradC. Another 32 bytes saved.

=REPT("M",A1/1E3)&REPT("D",MOD(A1,1E3)/500)&REPT("C",MOD(A1,500)/100)&REPT("L",MOD(A1,100)/50)&REPT("X",MOD(A1,50)/10)&REPT("V",MOD(A1,10)/5)&REPT("I",MOD(A1,5))

Formatted:

=REPT("M",A1/1E3)
    &REPT("D",MOD(A1,1E3)/500)
    &REPT("C",MOD(A1,500)/100)
    &REPT("L",MOD(A1,100)/50)
    &REPT("X",MOD(A1,50)/10)
    &REPT("V",MOD(A1,10)/5)
    &REPT("I",MOD(A1,5))

Wernisch

Posted 2017-06-27T13:08:38.403

Reputation: 2 534

You'll save some by replacing CONCATENATE with & between each element, and QUOTIENT with INT(A/B). – BradC – 2017-06-27T14:59:31.073

2 more savings: turns out REPT already truncates the number if it isn't an integer, so you can save 30 more bytes by removing each INT(). Save 2 more by replacing both 1000 with 1E3 (although Excel doesn't seem to want to keep it that way once you hit enter).

– BradC – 2017-06-27T16:21:38.563

Yeah, saw the 1E3 behaviour. Answer updated. – Wernisch – 2017-06-28T09:53:57.217

3

05AB1E, 29 26 25 bytes

¸5n3×Rvćy‰ì}"MDCLXVI"Ss×J

Try it online!

Explanation

¸                           # wrap input in a list
 5n                         # push 5**2
   3×                       # repeat it 3 times
     Rv                     # for each digit y in its reverse
       ć                    # extract the head of the list 
                            # (div result of the previous iteration, initially input)
        y‰                  # divmod with y
          ì                 # prepend to the list
           }                # end loop
            "MDCLXVI"S      # push a list of roman numerals
                      s×    # repeat each a number of times corresponding to the result
                            # of the modulus operations
                        J   # join to string

Emigna

Posted 2017-06-27T13:08:38.403

Reputation: 50 798

3

CJam, 35 28 bytes

-7 bytes thanks to Martin Ender

q~{5md\2md\}3*]W%"MDCLXVI".*

Try it online!

Explanation

q~         e# Read and eval input (push the input as an integer).
{          e# Open a block:
 5md\      e#  Divmod the top value by 5, and bring the quotient to the top.
 2md\      e#  Divmod that by 2, and bring the quotient to the top.
}3*        e# Run this block 3 times.
]W%        e# Wrap the stack in an array and reverse it. Now we've performed the mixed-base
           e# conversion.
"MDCLXVI"  e# Push this string.
.*         e# Element-wise repetition of each character by the numbers in the other array.
           e# Implicitly join and print.

Business Cat

Posted 2017-06-27T13:08:38.403

Reputation: 8 927

3

Perl 5, 66 bytes

65 bytes of code + -p flag.

$s=1e3;for$@(MDCLXVI=~/./g){$\.=$@x($_/$s);$_%=$s;$s/=--$|?2:5}}{

Try it online!

Without changing the byte count, MDCLXVI=~/./g can be replaced by M,D,C,L,X,V,I; and --$|?2:5 by $|--*3+2.

Much longer (99 bytes), there is:

$_=M x($_/1e3).D x($_%1e3/500).C x($_%500/100).L x($_%100/50).X x($_%50/10).V x($_%10/5).I x($_%5)

Dada

Posted 2017-06-27T13:08:38.403

Reputation: 8 279

3

C#, 127 bytes

f=n=>n>999?"M"+f(n-1000):n>499?"D"+f(n-500):n>99?"C"+f(n-100):n>49?"L"+f(n-50):n>9?"X"+f(n-10):n>4?"V"+f(n-5):n>0?"I"+f(n-1):""

A purely hard coded ternary statement using recursion.

Full/Formatted version:

using System;

class P
{
    static void Main()
    {
        Func<int, string> f = null;
        f = n => n > 999 ? "M" + f(n - 1000)
                         : n > 499 ? "D" + f(n - 500)
                                   : n > 99 ? "C" + f(n - 100)
                                            : n > 49 ? "L" + f(n - 50)
                                                     : n > 9 ? "X" + f(n - 10)
                                                             : n > 4 ? "V" + f(n - 5)
                                                                     : n > 0 ? "I" + f(n - 1)
                                                                             : "";

        Console.WriteLine(f(3));
        Console.WriteLine(f(4));
        Console.WriteLine(f(42));
        Console.WriteLine(f(796));
        Console.WriteLine(f(2017));
        Console.WriteLine(f(16807));

        Console.ReadLine();
    }
}

TheLethalCoder

Posted 2017-06-27T13:08:38.403

Reputation: 6 930

n>0 is just n. – CalculatorFeline – 2017-06-29T01:44:52.133

@CalculatorFeline Not in C#, an int can't be implicitly cast to a bool. – TheLethalCoder – 2017-06-29T08:30:20.440

That's unfortunate. – CalculatorFeline – 2017-06-29T15:23:01.257

@CalculatorFeline Yeah, C# is too strongly typed for it's own good sometimes. – TheLethalCoder – 2017-06-29T15:30:02.113

3

JavaScript (ES6), 81 75 69 Bytes

Saved 6 bytes thanks to @Neil for porting @Jörg Hülsermann's answer

Saved 6 bytes thanks to @Shaggy

n=>'MDCLXVI'.replace(/./g,(c,i)=>c.repeat(n/a,n%=a,a/=i%2?5:‌​2),a=1e3)

Test cases:

f=
n=>'MDCLXVI'.replace(/./g,(c,i)=>c.repeat(n/a,n%=a,a/=i%2?5:‌​2),a=1e3)

const testCases = [3, 4, 42, 796, 2017, 16807]
testCases.forEach(testCase => console.log(f(testCase)))

Craig Ayre

Posted 2017-06-27T13:08:38.403

Reputation: 217

1You should be able to move the n%=x within the repeat method to save a few bytes. – Shaggy – 2017-06-27T14:04:06.690

1FYI a port of the PHP answer is only 69 bytes: n=>'MDCLXVI'.replace(/./g,(c,i)=>c.repeat(n/a,n%=a,a/=i%2?5:2),a=1e3) – Neil – 2017-06-28T09:19:17.647

Thanks @Neil, I've updated the post. Removes the hard coded array which I was wanting to revisit – Craig Ayre – 2017-06-28T09:44:45.753

2

Python 3, 100 97 96 94 93 91 90 bytes

  • saved 4+2 bytes: use of def; array as default parameter reduced an indentation space; unwanted variable declaration removed
  • @shooqie saved 1 byte a%= shorthand
  • saved 2 bytes: rearranged and braces in (a//i) got removed
  • @Wondercricket saved 1 byte: move the array from default parameter to within the function which removed [] at the cost of one indentation space, thus saving 1 byte.
def f(a):
 b=1000,500,100,50,10,5,1
 for i in b:print(end=a//i*'MDCLXVI'[b.index(i)]);a%=i

Try it online!

officialaimm

Posted 2017-06-27T13:08:38.403

Reputation: 2 739

1a%=i is a byte shorter :) – shooqie – 2017-06-27T13:51:10.510

1You can also save a byte by storing b as a variable within the function. That removes the necessity of brackets - b=1000,500,100,50,10,5,1 – Wondercricket – 2017-06-27T21:10:51.793

2

///, 50 bytes

/1/I//IIIII/V//VV/X//XXXXX/L//LL/C//CCCCC/D//DD/M/

Try it online!

Takes input in unary, and I'm (ab)using the footer field on TIO for input so output is preceded by a newline.

steenbergh

Posted 2017-06-27T13:08:38.403

Reputation: 7 772

2

Cubix, 69 74 80 bytes

/.UI,..N&..0\0&/52"IVXLCDM"U,r%ws;rr3tu;pw..u;qrUosv!s.u\psq,!@Us(0;U

Try it online!

        / . U I
        , . . N
        & . . 0
        \ 0 & /
5 2 " I V X L C D M " U , r % w
s ; r r 3 t u ; p w . . u ; q r
U o s v ! s . u \ p s q , ! @ U
s ( 0 ; U . . . . . . . . . . .
        . . . .
        . . . .
        . . . .
        . . . .

Watch It Running

I have managed to compress it a bit more, but there is still some pesky no-ops, especially on the top face.

  • 52"IVXLCDM"U put the necessary divisors and characters on the stack. The 5 and 2 will be used to reduce the div/mod value and the characters will be discarded after use.
  • UIN0/&0\&,/U u-turns onto the top face and starts a long tour to get the input and push 1000 onto the stack. An initial divide is done and a u-turn onto the r of the next snippet. This was an area I was looking at to make some savings.
  • ,r%ws;rr beginning of the divmod loop. integer divide, rotate the result away mod, then rearrange top of stack to be reduced input, current divisor and divide result.
  • 3tus bring the current character to the top and swap it with the divide result.
  • !vsoUs(0;U this is the print loop. while the div result is more than 0, swap with character output, swap back, decrement, push a 0 and drop it. On 0 redirect over the pop stack (remove divide result) and around the cube.
  • \u;pwpsq,!@Urq;u with a bit of redirection, this removes the character from the stack, brings the 5 and 2 to the top, swaps them and pushes one back down. The remaining is used to reduce the divisor. Halt if it reduces to 0, otherwise push the 5 or 2 to the bottom and re-enter the loop.

MickyT

Posted 2017-06-27T13:08:38.403

Reputation: 11 735

1

Python 2, 109 90 bytes

lambda n,r=[1000,500,100,50,10,5,1]:''.join(n%a/b*c for a,b,c in zip([n+1]+r,r,'MDCLXVI'))

Try it online!

Rod

Posted 2017-06-27T13:08:38.403

Reputation: 17 588

1000 can be 1e3 (if you don't mind it being a float which shouldn't be a problem) – CalculatorFeline – 2017-06-27T15:09:16.607

@CalculatorFeline it would turn the result into a float, and you can't multiply a string by a float :c – Rod – 2017-06-27T15:40:10.407

1

Mathematica, 130 bytes

(f=#~NumberDecompose~{1000,500,100,50,10,5,1};""<>{Flatten@Table[Table[{"M","D","C","L","X","V","I"}[[i]],f[[i]]],{i,Length@f}]})&

J42161217

Posted 2017-06-27T13:08:38.403

Reputation: 15 931

1

PHP, 70 bytes

for($r=1e3;$a=&$argn;)$a/$r<1?$r/=++$i%2?2:5:$a-=$r*print MDCLXVI[$i];

Try it online!

Jörg Hülsermann

Posted 2017-06-27T13:08:38.403

Reputation: 13 026

1

T-SQL, 164 bytes

SELECT REPLICATE('M',n/1000)+IIF(n%1000>499,'D','')
      +REPLICATE('C',n%500/100)+IIF(n%100>49,'L','')
      +REPLICATE('X',n%50/10)+IIF(n%10>4,'V','')
      +REPLICATE('I',n%5)
FROM t

Line breaks added for readability only.

This version is a lot longer (230 characters), but feels much more "SQL-like":

DECLARE @ INT,@r varchar(99)=''SELECT @=n FROM t
SELECT'I's,1v INTO m
INSERT m VALUES('V',5),('X',10),('L',50),('C',100),('D',500),('M',1000)
L:
    SELECT @-=v,@r+=s 
    FROM m WHERE v=(SELECT MAX(v)FROM m WHERE v<=@)
IF @>0GOTO L
SELECT @r

Makes a table m with all the char-value mappings, and then loops through finding the largest value <= the number, concatenating the matching character.

BradC

Posted 2017-06-27T13:08:38.403

Reputation: 6 099

1

Japt, 34 bytes

"IVXLCD"£%(U/=Y=v *3+2Y)îXÃw i'MpU

Test it online!

"IVXLCD"£    %(U/=Y=v  *3+2Y )îXÃ w i'MpU
"IVXLCD"mXY{U%(U/=Y=Yv *3+2,Y)îX} w i'MpU : Ungolfed
                                          : Implicit: U = input number
"IVXLCD"mXY{                    }         : Map each char X and its index Y in this string to:
                  Y=Yv *3+2               :   Set Y to 5 for even indexes, 2 for odd.
               U/=                        :   Divide U by this amount.
            U%(            ,Y)            :   Modulate the old value of U by 5.
                              îX          :   Repeat the character that many times.
                                          : This returns e.g. "IIVCCCD" for 16807.
                                  w       : Reverse the entire string.
                                    i'MpU : Prepend U copies of 'M' (remember U is now the input / 1000).
                                          : Implicit: output result of last expression

ETHproductions

Posted 2017-06-27T13:08:38.403

Reputation: 47 880

1

JavaScript (ES6), 65 bytes

A recursive function.

f=(n,a=(i=0,1e3))=>n?a>n?f(n,a/=i++&1?5:2):'MDCLXVI'[i]+f(n-a):''

How?

The second recursive call f(n-a) really should be f(n-a,a). By omitting the 2nd parameter, a and i are reinitialized (to 1000 and 0 respectively) each time a new Roman digit is appended to the final result. This causes more recursion than needed but doesn't alter the outcome of the function and saves 2 bytes.

Test cases

f=(n,a=(i=0,1e3))=>n?a>n?f(n,a/=i++&1?5:2):'MDCLXVI'[i]+f(n-a):''

console.log(f(3))
console.log(f(4))
console.log(f(9))
console.log(f(42))
console.log(f(796))
console.log(f(2017))
console.log(f(16807))

Arnauld

Posted 2017-06-27T13:08:38.403

Reputation: 111 334

1

J, 26 23 bytes

3 bytes saved thanks to Adám.

'MDCLXVI'#~(_,6$2 5)&#:

Try it online!

Similar to the APL answer basically the same thing.

'MDCLXVI'#~(_,6$2 5)&#:
           (       )&#:   mixed base conversion from decimal
              6$2 5       2 5 2 5 2 5
            _,            infinity 2 5 2 5 2 5
                          this gives us e.g. `0 0 0 0 1 0 4` for input `14`
'MDCLXVI'#~               shape each according to the number of times on the right
                          this is greedy roman numeral base conversion

Conor O'Brien

Posted 2017-06-27T13:08:38.403

Reputation: 36 228

Not that I know J, but why #.inv instead of #:? – Adám – 2017-06-27T22:27:49.440

@Adám Ah, good point. I customarily use #.inv instead of #:, since something like 2 #: 4 is 0, whilst 2 #.inv 4 is 1 0 0 – Conor O'Brien – 2017-06-27T22:29:25.750

Yeah, I do the same thing in APL. Now your solution is truly equivalent to the APL solution. – Adám – 2017-06-27T22:30:32.323

# is /; ~ is ; $ is ; & is ; #: is . The only difference is that you use infinity _ while you could use 0 like the APL answer. – Adám – 2017-06-27T22:33:18.140

@Adám Huh, cool. – Conor O'Brien – 2017-06-27T22:37:57.657

1

Batch, 164 bytes

@set/pn=
@set s=
@for %%a in (1000.M 500.D 100.C 50.L 10.X 5.V 1.I)do @call:c %%~na %%~xa
@echo %s:.=%
@exit/b
:c
@if %n% geq %1 set s=%s%%2&set/an-=%1&goto c

Takes input on STDIN.

Neil

Posted 2017-06-27T13:08:38.403

Reputation: 95 035

1

Oracle SQL, 456 bytes

select listagg((select listagg(l)within group(order by 1)from dual start with trunc((n-nvl(n-mod(n,p),0))/v)>0 connect by level<=trunc((n-nvl(n-mod(n,p),0))/v)))within group(order by v desc)from (select 2849n from dual)cross join(select 1000v,null p,'m'l from dual union select 500,1000,'d'from dual union select 100,500,'c'from dual union select 50,100,'l'from dual union select 10,50,'x'from dual union select 5,10,'v'from dual union select 1,5,'i'from dual)

Outputs:

mmdcccxxxxviiii

Please note the actual size of the line is 460bytes, because it includes the input number (2849).

Ungolfed:

select listagg(
            (select listagg(l, '') within group(order by 1) 
             from dual 
             start with trunc((n-nvl(p*trunc(n/p),0))/v) > 0 
             connect by level <= trunc((n-nvl(p*trunc(n/p),0))/v) )
        ) within group(order by v desc)
from (select 2348 n
    from dual
) cross join (
    select 1000v, null p, 'm' l from dual union 
    select 500, 1000, 'd' from dual union
    select 100, 500, 'c' from dual union
    select 50, 100, 'l' from dual union
    select 10, 50, 'x' from dual union
    select 5, 10, 'v' from dual union
    select 1, 5, 'i' from dual     
)

How it works: I calculate how many of each letter I need, by calculating the most I can get to with the higher value one (infinity for M), and then doing an integer division between the current letter's value and the result of that.

E.g. 2348, how many Cs do I need? trunc((2348-mod(2348,500))/100) = 3.

Then, I listagg that letter together 3 times (exploiting CONNECT BY to generate the 3 rows I need). Finally, I listagg everything together.

Kinda bulky, but most of it is the select from duals in the conversion table and I can't really do much about that...

Demonblack

Posted 2017-06-27T13:08:38.403

Reputation: 121

0

Charcoal, 61 50 46 bytes

NνA⁰χWφ«W¬‹νφ«§MDCLXVIχA⁻νφν»A⁺¹χχA÷φ⎇﹪χ²¦²¦⁵φ

Try it online!

Explanation:

Nν                   Take input as number and assign it to ν
A⁰χ                  Let χ=0
Wφ«                  While φ>0 (φ has a predefined value of 1000)
    W¬‹νφ«               While v>=φ 
        §MDCLXVIχ             Take the char from string "MDCLXVI" at position χ
        A⁻νφν»               Let ν=ν-φ
    A⁺¹χχ                Increment χ
    A÷φ⎇﹪χ²¦²¦⁵φ        If χ is odd, divide φ by 5, else divide φ by 2
  • 4 bytes saved thanks to Neil, while I am still trying to figure out how to proceed with the second part of his comment.

Charlie

Posted 2017-06-27T13:08:38.403

Reputation: 11 448

1Nν is one byte shorter than ANν, ¬‹ is one byte shorter than subtracting 1, and if you use ÷ (IntDivide) instead of (Divide) then you can use φ as the outer loop condition. However, I think you can get it down to 40 bytes by looping over MDCLXVI directly instead. – Neil – 2017-06-27T15:49:40.860

@Neil of course, silly me, trying to understand why there is no "greater or equal" operator when I could use "not less". Very clever trick the use of the integer division. Now allow me some time to think about the last part of your comment... – Charlie – 2017-06-27T20:27:40.647

I improved my string loop idea and posted it as a seprate answer along with a port of @xnor's Python answer, which turned out to be the same length. – Neil – 2017-06-28T09:32:29.090

0

Java (OpenJDK 8), 119 118 bytes

n->{String s="";for(int v[]={1,5,10,50,100,500,1000},i=7;i-->0;)for(;n>=v[i];n-=v[i])s+="IVXLCDM".charAt(i);return s;}

Try it online!

Saved a byte thanks to @TheLethalCoder

Olivier Grégoire

Posted 2017-06-27T13:08:38.403

Reputation: 10 647

1Can you declare v and i in the first for loop to save a byte? – TheLethalCoder – 2017-06-27T15:05:45.150

@TheLethalCoder Yes, most certainly. I was on a totally other idea at first that this didn't pass my internal review :p – Olivier Grégoire – 2017-06-27T15:07:08.037

0

Charcoal, 34 bytes

NςA²ξFMDCLXVI«×ι÷ςφA﹪ςφςA÷φξφA÷χξξ

Originally based on @CarlosAlego's answer. A port of @xnor's Python solution is also 34 bytes:

NθA⁵ξFIVXLCD«←×ι﹪θξA÷θξθA÷χξξ»←×Mθ

Edit: A port of @xnor's other Python solution turns out to be 33 bytes!

NθFMDCLXVI«×ι÷θφA﹪θφθA÷φ⁺׳﹪φ³±¹φ

Try it online! Link is to verbose version of code. Note that I've used ⁺׳﹪φ³±¹ instead of ⁻׳﹪φ³¦¹ because the deverbosifier is currently failing to insert the separator.

Neil

Posted 2017-06-27T13:08:38.403

Reputation: 95 035

1Huh, that looks more Greek than Roman. – Adám – 2017-06-28T09:33:21.133

0

C++, 272 Bytes

#include <cstdio>
#include <map>
std::map<int,char> m = {{1000,'M'},{500,'D'},{100,'C'},{50,'L'},{10,'X'},{5,'V'},{1,'I'}};
int main(){unsigned long x;scanf("%d",&x);for(auto i=m.rbegin();i!=m.rend();++i)while(x>=i->first){printf("%c", i->second);x=x-i->first;}return 0;}

HSchmale

Posted 2017-06-27T13:08:38.403

Reputation: 181

0

C, 183 Bytes

#include <stdio.h>
int v[]={1000,500,100,50,10,5,1};
char*c="MDCLXVI";
int main(){int x;scanf("%d",&x);for(int i=0;i<sizeof v/sizeof(int);i++)for(;x>=v[i];x-=v[i])putc(c[i],stdout);}

Same algorithm as before, just using plain c arrays instead of an std::map, partially inspired by @xnor's answer and using a string to store the letters.

HSchmale

Posted 2017-06-27T13:08:38.403

Reputation: 181

98 bytes – ceilingcat – 2019-11-11T19:29:55.700

0

Common Lisp, 113 bytes

This is an anonymous function, returning the result as a string.

(lambda(value)(setf(values a b)(floor v 1000))(concatenate 'string(format()"~v,,,v<~>"a #\M)(format nil"~@:r"b)))

Ungolfed, with descriptive variable names and comments:

(defun format-roman (value)
  ;; Get "value integer-divided by 1000" and "value mod 1000"
  (setf (values n_thousands remainder) (floor value 1000))
  (concatenate 'string
               ;; Pad the empty string n_thousands times, using "M" as the 
               ;; padding character
               (format () "~v,,,v<~>" n_thousands #\M)
               ;; Format the remainder using "old-style" Roman numerals, i.e. 
               ;; numerals with "IIII" instead of "IV"
               (format nil "~@:r" remainder)))

CL has built-in Roman numeral formatter. Sadly it doesn't work for numbers larger than 3999.

shadowtalker

Posted 2017-06-27T13:08:38.403

Reputation: 461