Code Golf New Year Edition - Integer to Roman Nume

2019-01-18 03:08发布

问题:

Write a program that take a single command line argument N and prints out the corresponding Roman Numeral.

Eg N = 2009 should print MMIX.

Let's say this should work for 0 < N < 3000.

(Had fun playing my first ever round of code golf with the Christmas edition, and thought this could fit for New Year. Googled to see if this has come up before elsewhere and it looks like it hasn't, but let me know if this is too hard or too easy or if the rules need changing. )

Happy MMIX!

回答1:

Perl: 69 strokes (count 'em!)

Sixty-nine strokes including calling perl in the first place:

$ perl -ple's!.!($#.=5x$&*8%29628)=~y$IVCXL4620-8$XLMCDIXV$d!eg;last}{'
3484
MMMCDLXXXIV
  • Reads a single line, writes a single line.
  • Works from 0 to 3999, inclusive. (Prints empty string for 0.)
  • In Perl golf competitions, this is usually scored as 62 strokes = 58 for the code + 4 for the switches.
  • Why, yes, those are mismatched braces. Thanks for asking. =)

Credits: originally due to Ton Hospel. The trick involving the mismatched braces is from rev.pl in this post (which incidentally, is ingenious).



回答2:

In C#, as an extension method to Int32:

public static class Int32Extension {
    public static string ToRomanNumeral(this int number) {
        Dictionary<int, string> lookup = new Dictionary<int, string>() {
            { 1000000, "M_" },
            { 900000, "C_D_" },
            { 500000, "D_" },
            { 400000, "C_D_" },
            { 100000, "C_" },
            { 90000, "X_C_" },
            { 50000, "L_" },
            { 40000, "X_L_" },
            { 10000, "X_" },
            { 9000, "MX_"},
            { 5000, "V_" },
            { 4000, "MV_" },
            { 1000, "M" },
            { 900, "CM" },
            { 500, "D" },
            { 400, "CD" },
            { 100,"C" },
            { 90, "XC" },
            { 50, "L" },
            { 40, "XL" },
            { 10, "X" },
            { 9, "IX" },
            { 5, "V" },
            { 4, "IV" },
            { 1, "I" }
        };

        StringBuilder answer = new StringBuilder();
        foreach (int key in lookup.Keys.OrderBy(k => -1 * k)) {
            while (number >= key) {
                number -= key;
                answer.Append(lookup[key]);
            }
        }
        return answer.ToString();
    }
}

The underscores should be overlines above the respective letter to be true Roman Numeral.



回答3:

Common lisp (SBCL). 63 characters counted by "wc -c".

(format t "~@R~%" (parse-integer (elt *posix-argv* 1)))
(quit)

This only works for numbers upto 3999.



回答4:

C#: 179 chars (not including spaces/tabs)

    static string c(int a)
    {
        int[] v = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        var s = ""; 
        for ( var i = 0; i < 13; i++ )
            while (a >= v[i])
            {
                a -= v[i];
                s += "M CM D CD C XC L XL X IX V IV I".Split()[i];
            }
        return s;
    }


回答5:

Perl, 19 strokes. Guaranteed to work for values between 1 and 12.

sub r{chr 8543+pop}


回答6:

Language: JavaScript.

129 chars without the added formatting

The following code is a result of coding quiz which which took place at pl.comp.lang.javascript newsgrup several years ago. I'm not the author of the code.

function rome(N,s,b,a,o){
  for(s=b='',a=5;N;b++,a^=7)for(o=N%a,N=N/a^0;o--;)
  s='IVXLCDM'.charAt(o>2?b+N-(N&=~1)+(o=1):b)+s;return s
}

Original post by Elus



回答7:

Python, 173 bytes.

r=lambda n:o[n]if n<10 else''.join(dict(zip('ivxlc','xlcdm'))[c]for c in r(n//10))+o[n%10]
o=' i ii iii iv v vi vii viii ix'.split(' ')
import sys
print r(int(sys.argv[1]))

(I first saw this algorithm in Gimpel's Algorithms in Snobol4; Snobol expressed it more elegantly.)



回答8:

Language: C, Char count: 174

#define R(s,v)for(;n>=v;n-=v)printf(#s);
main(int n,char**a){n=atoi(a[1]);R(M,1000)R(CM,900)R(D,500)R(CD,400)R(C,100)R(XC,90)R(L,50)R(XL,40)R(X,10)R(IX,9)R(V,5)R(IV,4)R(I,1)}


回答9:

Pike

60 characters, valid for 0 to 10000:

int main (int c, array a) {
    write(String.int2roman((int)a[1]));
}


回答10:

Perl 5.10

perl -nE'@l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
$o="";while(@l){$o.=pop(@l)x($_/($c=pop @l));$_%=$c;}say$o'

You input a line, it gives you the Roman numeral equivelent. This first version even lets you input more than one line.

Here is a shorter version that only works for one line, and ignores edge cases. so 4 becomes IIII instead of IV.

perl -nE'@l=qw{1 I 5 V 10 X 50 L 100 C 500 D 1000 M};
while(@l){$o.=pop(@l)x($_/($c=pop @l));$_%=$c;}say$o'

Here is what the first version would look like as a Perl script.

use 5.010;
while(<>){
  @l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
  $o="";
  while(@l){
    $o .= pop(@l) x ($_/($c=pop @l));
    # $l = pop @l;
    # $c = pop @l;
    # $o .= $l x ($_/$c);

    $_ %= $c;
  }
  say $o;
}


回答11:

A simple Haskell version, that still keeps clarity. 205 characters, including white space.

l = ["M","CM","L","CD","C","XC","L","XL","X","IX","V","IV","I"]
v = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
roman n i
    | n == 0 = ""
    | n >= v!!i = l!!i ++ roman (n-v!!i) i
    | otherwise = roman n (i+1)


回答12:

In Python - taken from ActiveState (credits: Paul Winkler) and compressed a bit:

def int2roman(n):
   if not 0 < n < 4000: raise ValueError
   ints = (1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1)
   nums = ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
   result = ""
   for i in range(len(ints)):
      count = int(n / ints[i])
      result += nums[i] * count
      n -= ints[i] * count
   return result


回答13:

Perl, 145 strokes (if you strip out all the newlines, which are optional), valid for 1..3999:

%t=qw(1000 M 900 CM 500 D 400 CD 100 C 90 XC 50 L 40 XL 10 X 9 IX 5 V 4 IV 1 I);
$d=pop;
for(sort{$b<=>$a}keys%t){$r.=$t{$_}x($d/$_);$d%=$_}
print$r

Some would say I could use say, but I don't have a say-capable Perl version here. Feel free to subtract 2 off the stroke count if using say works. :-)

For non-Perl programmers, this program exploits a number of useful Perl features:

  1. Hashes are constructed from lists of even length.
  2. Lists of strings can be specified in a compact syntax, using qw.
  3. Strings can auto-coerce into integers, as used in the <=> comparison operator in sorting the keys.
  4. There is an x operator which makes copies of strings/lists. Unfortunately for golfing here, x has identical precedence to /; if / were higher, the brackets would have been optional too.


回答14:

Ruby, 136 chars

n = $*[0].to_i
for k,v in [1e3,900,500,400,100,90,50,40,10,9,5,4,1].zip %w{M CM D CD C XC L XL X IX V IV I}
  until n < k
    n -= k
    print v
  end
end


回答15:

Language: dc (through shell) Char count:122

EDIT: q is equivalent of 2Q

dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]?1000[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1

EDIT: two more chars by optimizing main loop stack manipulations

dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]?1000[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1

EDIT: save 2 chars

dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]1000?[sz2Q]sq[r~r[d0!<qSardPrLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1

Previous version:

dc -e '[I]1[IV]4[V]5[IX]9[X]10[XL]40[L]50[XC]90[C]100[CD]400[D]500[CM]900[M]1000?[sz2Q]sq[r~r[d0!<qSaSadPLaLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1


回答16:

I'm no Haskell expert, and this is too long to be a winner, but here's a solution I wrote a while back to solve Euler #89.

toRoman 0 = ""
toRoman 1 = "I"
toRoman 2 = "II"
toRoman 3 = "III"
toRoman 4 = "IV"
toRoman n
| n >= 1000 = repeatRoman 'M' 1000
| n >= 900 = subtractRoman "CM" 900
| n >= 500 = subtractRoman "D" 500
| n >= 400 = subtractRoman "CD" 400
| n >= 100 = repeatRoman 'C' 100
| n >= 90 = subtractRoman "XC" 90
| n >= 50 = subtractRoman "L" 50
| n >= 40 = subtractRoman "XL" 40
| n >= 10 = repeatRoman 'X' 10
| n >= 9 = subtractRoman "IX" 9
| n >= 5 = subtractRoman "V" 5
| otherwise = error "Hunh?"
where
    repeatRoman c n' = (take (n `div` n') (repeat c)) ++ (toRoman $ n `mod` n')
    subtractRoman s n' = s ++ (toRoman $ n - n')


回答17:

J, 20 characters!

'MDCLXVI'#~(7$5 2)#:

Usage:

   'MDCLXVI'#~(7$5 2)#: 2009
MMVIIII

Okay, it doesn't do subtraction properly, but hey it's pretty cool!

Explanation;

(7$5 2)

This takes the right argument (the list 5 2) and turns it into a list of size 7 - namely 5 2 5 2 5 2 5.

(7$5 2)#: 2009

This does the "anti-base" operation - basically doing iterative div and mod operations, returning the list 2 0 0 0 0 0 1 4.

Then #~ uses the previous list as a tally to pull corresponding characters out of 'MDCLXVI'.



回答18:

From a vaguely C-like language called LPC (precursor of Pike):

string roman_numeral(int val) {
    check_argument(1, val, #'intp);
    unless(val)
        return "N";
    string out = "";
    if(val < 0) {
        out += "-";
        val = -val;
    }
    if(val >= 1000) {
        out += "M" * (val / 1000);
        val %= 1000;
    }
    if(val >= 100) {
        int part = val / 100;
        switch(part) {
        case 9      :
            out += "CM";
            break;
        case 6 .. 8 :
            out += "D" + ("C" * (part - 5));
            break;
        case 5      :
            out += "D";
            break;
        case 4      :
            out += "CD";
            break;
        default     :
            out += "C" * part;
            break;
        }
        val %= 100;
    }
    if(val >= 10) {
        int part = val / 10;
        switch(part) {
        case 9      :
            out += "XC";
            break;
        case 6 .. 8 :
            out += "L" + ("X" * (part - 5));
            break;
        case 5      :
            out += "L";
            break;
        case 4      :
            out += "XL";
            break;
        default     :
            out += "X" * part;
            break;
        }
        val %= 10;
    }
    switch(val) {
    case 9      :
        out += "IX";
        break;
    case 6 .. 8 :
        out += "V" + ("I" * (val - 5));
        break;
    case 5      :
        out += "V";
        break;
    case 4      :
        out += "IV";
        break;
    default     :
        out += "I" * val;
        break;
    }
    return out;
}


回答19:

Python, 190 bytes. Based on snippet from ActiveState, via Federico.

A few small optimisations: removal of superfluous int() call, splitting string to get array, remove whitespace, ...

import sys
n=int(sys.argv[1])
N=(1000,900,500,400,100,90,50,40,10,9,5,4,1)
r=""
for i in range(len(N)):
 c=n/N[i]
 r+='M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I'.split(',')[i]*c
 n-=N[i]*c
print r

EDIT: superfluous, not spurious, and remove range check - thanks to Chris and dreeves! Stole idea of using symbol array inline from balabaster.



回答20:

VB: 193 chars

Function c(ByVal a)
    Dim v() = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
    Dim s = ""
    For i = 0 To 12
        While a >= v(i)
            a -= v(i)
            s += "M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I".Split("|")(i)
        End While
    Next
    Return s
End Function


回答21:

Excel 8 chars (not lincluding the number):

=ROMAN(N)

Works up to 3000.

Tongue in cheek



回答22:

Real simple: pass the query to Google and screenscrape the answer. Next. :p

BTW, shouldn't this be a community wiki?



回答23:

Java: 286 significant characters

public class R {

    String[]x="M,CM,D,C,XC,L,X,IX,V,I".split(",");
    int[]n={1000,900,500,100,90,50,10,9,5,1};

    String p(String s,int d,int i){return 10<=i?s:n[i]<=d?p(s+x[i],d-n[i],i):p(s,d,i+1);}

    public static void main(String[] a) {
        System.out.println(new R().p("",Integer.parseInt(a[0]),0));
    }

}

By "significant characters", I mean the printing characters and required spaces (e.g. between type and argument), but not pure cosmetic whitespace (newlines and indentation).



回答24:

Delphi (or Pascal, there's nothing Delphi-specific here):

Function ToRoman(N : Integer) : String;

Const
    V : Array [1..13] of Word = (1000,900,500,400,100,90,50,40,10.9,5,4,1);
    T : Array [1..13] of String = ('M','CM','D','CD','C','XC','L','XL','X','IX','V','I');

Var I : Word;

Begin
    I := 1;
    Repeat
        While N < V[I] do Inc(I);
        Result := Result + T[I];
        N := N - V[I];
    Until N = 0;
End;

How is everyone getting the character counts? (I count 8 essential spaces, all the rest are simply for formatting.)



回答25:

Here is a C solution in 252 meaningful chars. Valid from 0 <= i < 4000. Mostly I wrote this because so many solutions include IV and IX at array points. Decoding it: t is our temp buffer that we back fill so that we don't have to reverse it on output. The buffer passed in must be at least 16 chars (for 3888 -> MMMDCCCLXXXVIII).

 char* i2r(int i, char* r) {
     char t[20];
     char* o=t+19;*o=0;
     char* s="IVXLCDMM";
     for (char*p=s+1;*p&&i;p+=2) {
         int x=i%10;
         if (x==9) {*--o=p[1];*--o=p[-1];}
         else if (x==4) {*--o=*p;*--o=p[-1];}
         else {
             for(;x&&x!=5;--x)*--o=p[-1];
             if(x)*--o=*p;
         }
         i/=10;
     }
     return strcpy(r,o);
 }

And I always forget to put the main on. So much for 252 chars:

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 void main(int a,char**v){
     char buf[16];
     printf("%s\n",i2r(atoi(v[1])));
 }


回答26:

Language: C, Char count: 195

Based heavily off of me.yahoo.com/joe_mucchielle's C solution:

char t[99],*o=t+99,*s="IVXLCDMM",*p,x;n(v){*--o=p[v];}main(int i,int**v){i=atoi(v[1]);*o=0;
for(p=s+1;*p&&i;p+=2){x=i%10;if(x%5==4)n(x==9),n(-1);else{for(;x%5;--x)n(-1);if(x)n(0);}i/=10;}puts(o);}


回答27:

Language: Erlang, Char count: 222

EDIT2: Erlang preprocessor allows some sort of unbalanced macros so this version is 9 chars shorter.

-module(n2).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)];).
y(N)->io:format(n(N)).
?D(1000,M)?D(900,CM)?D(500,D)?D(400,CD)?D(100,C)?D(90,XC)?D(50,L)?D(40,XL)?D(10,X)?D(9,IX)?D(5,V)?D(4,IV)?D(1,I)n(0)->[10].

EDIT: Shorter version inspired by Darius version (231 chars)

-module(n).
-export([y/1]).
y(N)->io:format([n(N),10]).
n(N)when N>9->[Y||C<-n(N div 10),{K,Y}<-lists:zip("IVXLC","XLCDM"),K==C]++o(N rem 10);n(N)->o(N).
o(N)->lists:nth(N+1,[[]|string:tokens("I II III IV V VI VII VIII IX"," ")]).

It's less readable but save 2 chars (233 chars).

-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y(N)->io:format(n(N)).
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].

Command line version:

-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y([N])->io:format(n(list_to_integer(N))),init:stop().
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].

Invocation:

$ erl -noshell -noinput -run n y 2009
MMIX

EDIT: I saved 17 chars using literal macro expansion.



回答28:

Railo CFML - 53 chars, 46 without whitespace...

<cfoutput>
    #NumberFormat( N , 'roman' )#
</cfoutput>


Or, for other CF engines, not sure if these are shortest, but they'll do for now...


CFML - 350..453 characters:

<cffunction name="RomanNumberFormat">
    <cfset var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I') />
    <cfset var I = [1000,900,500,100,90,50,10,9,5,4,1] />
    <cfset var R = '' />
    <cfset var x = 1 />
    <cfset var A = Arguments[1] />
    <cfloop condition="A GT 0">
        <cfloop condition="A GTE I[x]">
            <cfset R &= D[x] />
            <cfset A -= I[x] />
        </cfloop>
        <cfset x++ />
    </cfloop>
    <cfreturn R />
</cffunction>

<cfoutput>
    #RomanNumberFormat(N)#
</cfoutput>


CFScript - 219..323 characters:

<cfscript>
    function RomanNumberFormat(A)
    {
        var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I');
        var I = [1000,900,500,100,90,50,10,9,5,4,1];
        var R = '';
        var x = 1;
        while ( A > 0 )
        {
            while( A >= I[x] )
            {
                R &= D[x];
                A -= I[x];
            }
            x++;
        }
        return R;
    }

    WriteOutput( RomanNumberFormat(N) );
</cfscript>


回答29:

In C# (running on .NET 4 RC), with 335 chars (if you remove the extraneous formatting).

using System;
using System.Linq;
class C
{
    static void Main()
    {
        Console.WriteLine(
                Console.ReadLine()
                .PadLeft(4,'0')
                .Select(d=>d-'0')
                .Zip(new[]{"  M","MDC","CLX","XVI"},(x,y)=>new{x,y})
                .Aggregate("",(r,t)=>r+
                                    new string(t.y[2],t.x%5/4)+
                                    new string(t.y[0],t.x%5/4*t.x/5)+
                                    new string(t.y[1],Math.Abs(t.x%5/4-t.x/5))+
                                    new string(t.y[2],t.x%5%4)));
    }
}

I know it does not beat the current best C# answer (182 chars) but this is just one big LINQ one-liner. Once I saw a raytracer written as a single LINQ query, I started approaching code golfs from this perspective.

Since this approach is functional, I'm working on a Haskell version of the same algorithm (will surely be shorter).



回答30:

Haskell version of my C#/LINQ answer, 234 chars:

main=putStrLn.foldr(\(y,x)s->concat[s,r(a x)(y!!2),r(a x*div x 5)(y!!0),r(abs(a x-div x 5))(y!!1),r(mod(mod x 5)4)(y!!2)])"".zip["XVI","CLX","MDC","  M"].map(read.(:[])).take 4.(++"000").reverse=<<getLine
r=replicate
a x=div(mod x 5)4