SUBARAM RAM
SUBARAM RAM

Reputation: 65

Converting the Numbers into equivalent Roman Numerals

I have tried the Number Conversion into its equivalent Roman numerals. Please suggest me to reduce the implementation of the code for getting output. And also please suggest the same scenario in the Erlang (mentioned in the tags) it will be more helpful to understand the Erlang.

Tried Code:

***Roman = {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"}
    def sub(Num,Num2):
      Num1 = Num - Num2
      N = Num - Num1
      return N
    def roman_convert(Num,roman) :
      #print(roman)
      while( Num > 0):
        if Num >= 1000:
           N = sub(Num,1000)
           roman.append(Roman.get(N))
           Num = Num - 1000
        elif Num >= 900:
           N = sub(Num,900)
           roman.append(Roman.get(N))
           Num = Num - 900
        elif Num >= 500:
            N = sub(Num,500)
            roman.append(Roman.get(N))
            Num = Num - 500
        elif Num >= 400:
           N = sub(Num,400)
           roman.append(Roman.get(N))
           Num = Num - 400
        elif Num >= 100:
           N = sub(Num,100)
           roman.append(Roman.get(N))
           Num = Num - 100
        elif Num >= 90:
           N = sub(Num,90)
           roman.append(Roman.get(N))
           Num = Num - 90
        elif Num >= 50:
           N = sub(Num,50)
           roman.append(Roman.get(N))
           Num = Num - 50 
        elif Num >= 40:
           N = sub(Num,40)
           roman.append(Roman.get(N))
           Num = Num - 40   
        elif Num >= 10:
           N = sub(Num,10)
           roman.append(Roman.get(N))
           Num = Num - 10        
        elif Num >= 9:
           N = sub(Num,9)
           roman.append(Roman.get(N))
           Num = Num - 9
        elif Num >= 4:
           N = sub(Num,4)
           roman.append(Roman.get(N))
           Num = Num - 4
        elif Num >= 1:
           N = sub(Num,1)
           roman.append(Roman.get(N))
           Num = Num - 1
        else :
           roman.append(0) 
      return roman
    if __name__ == "__main__":
      print("Converting the Numbers into Roman:")
      Num = int(input("Enter the number :"))
      roman =[]
      R = roman_convert(Num,roman)
      Roman = "".join(str(x) for x in R)
      print(Roman)***

Please suggest me how to improvise the code ?

Output of the above code is below

**Converting the Numbers into Roman:
Enter the number :199
CXCIX**

Upvotes: 1

Views: 260

Answers (4)

Alain T.
Alain T.

Reputation: 42133

I don't know Erlang but this recursive Python version may help:

romVL = [(1000,"M"),(500,"D"),(100,"C"),(50,"L"),(10,"X"),(5,"V"),(1,"I")]
def int2rom(N,i=0):
    if not N: return ""
    V0,L0 = romVL[i]
    k,R   = divmod(N,V0)
    for V1,L1 in romVL[-1:i:-1]:                      # subtractive
        if 2*V1<V0 and R-V0+V1 in range(V1):
            return L0*k+L1+L0 + int2rom(R-V0+V1,i+1)
    return L0*k+int2rom(R,i+1)                        # additive

output:

int2rom(199)  'CIC'

int2rom(1938) 'MCMXXXVIII'

int2rom(1988) 'MLMXXXVIII'

int2rom(1999) 'MIM'

int2rom(2019) 'MMXIX'

The way it works is by dealing with one order of magnitude at each recursion level. The function generates the appropriate number of letters (k = N//V0 from divmod) and recurses for the remainder (R) of the number (int2rom(R,i+1)).

For example: 2350 --> MM,350 --> CCC,50 --> L,0 :: MMCCCL

The subtractive part is handled after figuring out the number of letters but before sending the remainder down the recursion. It starts from the smallest order of magnitude and goes up to the current letter (for V1,L1 in romVL[-1:i:-1]). The first subtractive value that leaves a number smaller than itself (R-V0+V1 in range(V1)) is used and the recursion goes down from the remainder of the current letter minus the subtracted letter int2rom(R-V0+V1,i+1).

For example: 296 --> CC,97 ... VC,1 --> II,0 :: CCVCII

In this case, after choosing to use CC (200) and before recursing for smaller letters, the function tries to see if it can use any ?C to consume a larger part of the remaining value (96). It first tries with 'I', but IC is 99 so it is too large, then VC which is 95 and would leave 2 as the remainder (which is smaller than the value of V). Now the remainder is 2 and the next level of recursions converts it to II.

There is one exception to the subtractive part which is that it needs to avoid subtracting a letter that could simply be used directly at the next level. For example 26 should not be converted to XXVXI because we can use V directly to get XXVI. To avoid this, letters that have a value exactly half of the current letter are excluded from the subtractive process (i.e. VX, LC, DM) using 2*V1<V0

Note that you can apply the subtractive parts directly in the romVL list ahead of time and only keep the additive logic in the function. This makes the mapping list larger and adds extra levels of recursion but it allows for a much simpler function implementation.

romVL = [999, 'IM', 995, 'VM', 990, 'XM', 950, 'LM', 900, 'CM', 500, 'D', 499, 'ID', 495, 'VD', 490, 'XD', 450, 'LD', 400, 'CD', 100, 'C', 99, 'IC', 95, 'VC', 90, 'XC', 50, 'L', 49, 'IL', 45, 'VL', 40, 'XL', 10, 'X', 9, 'IX', 5, 'V', 4, 'IV', 1, 'I']

def int2rom(N,V=1000,L="M",*rest):
    return N//V*L + int2rom(N%V,*rest or romVL) if N else ""

Upvotes: 1

Pascal
Pascal

Reputation: 14042

Erlang solution using the algorithm shown by @Rajat:

1> Conv = [{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"}].
[{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"}]
2> ToR_ = fun ToR_(0,_,Acc) -> Acc; ToR_(X,C=[{N,S}|T],Acc) -> case X div N of 0 -> ToR_(X,T,Acc); _ -> ToR_(X-N,C,Acc ++ S) end end.
#Fun<erl_eval.18.97283095>
3> ToR = fun(X) when X > 0, X < 5000 -> ToR_(X,Conv,[]) end.
#Fun<erl_eval.44.97283095>
4> ToR(25).                                                 
"XXV"
5> ToR(2098).                                                                                                                            
"MMXCVIII"

Upvotes: 1

Agus
Agus

Reputation: 689

Here is the solution for Erlang with some notes:

  1. Can only handle up to integer 3999. If you need more than that, you can expand the code and add the map values accordingly.
  2. Test scenario is based on the table titled Roman Numerals Conversion Table in this link.

Test Result:

1 -> "I"
2 -> "II"
3 -> "III"
4 -> "IV"
5 -> "V"
6 -> "VI"
7 -> "VII"
8 -> "VIII"
9 -> "IX"
10 -> "X"
11 -> "XI"
12 -> "XII"
13 -> "XIII"
14 -> "XIV"
15 -> "XV"
16 -> "XVI"
17 -> "XVII"
18 -> "XVIII"
19 -> "XIX"
20 -> "XX"
21 -> "XXI"
22 -> "XXII"
23 -> "XXIII"
24 -> "XXIV"
25 -> "XXV"
30 -> "XXX"
35 -> "XXXV"
40 -> "XL"
45 -> "XLV"
50 -> "L"
55 -> "LV"
60 -> "LX"
65 -> "LXV"
70 -> "LXX"
75 -> "LXXV"
80 -> "LXXX"
85 -> "LXXXV"
90 -> "XC"
95 -> "XCV"
100 -> "C"
105 -> "CV"
110 -> "CX"
115 -> "CXV"
120 -> "CXX"
125 -> "CXXV"
130 -> "CXXX"
135 -> "CXXXV"
140 -> "CXL"
145 -> "CXLV"
150 -> "CL"
175 -> "CLXXV"
200 -> "CC"
225 -> "CCXXV"
250 -> "CCL"
275 -> "CCLXXV"
300 -> "CCC"
325 -> "CCCXXV"
350 -> "CCCL"
375 -> "CCCLXXV"
400 -> "CD"
425 -> "CDXXV"
450 -> "CDL"
475 -> "CDLXXV"
500 -> "D"
525 -> "DXXV"
540 -> "DXL"
550 -> "DL"
575 -> "DLXXV"
600 -> "DC"
625 -> "DCXXV"
650 -> "DCL"
675 -> "DCLXXV"
700 -> "DCC"
750 -> "DCCL"
825 -> "DCCCXXV"
900 -> "CM"
975 -> "CMLXXV"
1000 -> "M"
1050 -> "ML"
1125 -> "MCXXV"
1200 -> "MCC"
1275 -> "MCCLXXV"
1350 -> "MCCCL"
1425 -> "MCDXXV"
1500 -> "MD"
1575 -> "MDLXXV"
1650 -> "MDCL"
1725 -> "MDCCXXV"
1800 -> "MDCCC"
1875 -> "MDCCCLXXV"
1950 -> "MCML"
2025 -> "MMXXV"
2100 -> "MMC"
2175 -> "MMCLXXV"
2250 -> "MMCCL"
2325 -> "MMCCCXXV"
2400 -> "MMCD"
2475 -> "MMCDLXXV"
2550 -> "MMDL"
2700 -> "MMDCC"
3000 -> "MMM"
3400 -> "MMMCD"
2450 -> "MMCDL"
3500 -> "MMMD"
3900 -> "MMMCM"
3950 -> "MMMCML"
3999 -> "MMMCMXCIX"

Code:

-module(roman). 

-export([convert_to_roman/1]).
-export([test_convert_roman/1]).

-define(BASIC_ROMAN_NUMERAL_MAP, #{
                               0 => "",
                               1 => "I",
                               2 => "II",
                               3 => "III",
                               4 => "IV",
                               5 => "V",
                               6 => "VI",
                               7 => "VII",
                               8 => "VIII",
                               9 => "IX",
                               10 => "X",
                               20 => "XX",
                               30 => "XXX",
                               40 => "XL",
                               50 => "L",
                               60 => "LX",
                               70 => "LXX",
                               80 => "LXXX",
                               90 => "XC",
                               100 => "C",
                               200 => "CC",
                               300 => "CCC",
                               400 => "CD",
                               500 => "D",
                               600 => "DC",
                               700 => "DCC",
                               800 => "DCCC",
                               900 => "CM",
                               1000 => "M",
                               2000 => "MM",
                               3000 => "MMM"
                                }). 

convert_to_roman(Int) when Int > 0, Int < 4000 ->
    ThousandDiv = Int div 1000,
    Thousands = ThousandDiv * 1000,
    case ThousandDiv of
        0 -> ThousandsStr = "";
        _ -> ThousandsStr = maps:get(Thousands, ?BASIC_ROMAN_NUMERAL_MAP, "Unmapped1000")
    end,

    ThousandRem = Int - Thousands, 
    HundredDiv = ThousandRem div 100,
    Hundreds = HundredDiv * 100,
    case HundredDiv of
        0 -> HundredsStr = "";
        _ -> HundredsStr = maps:get(Hundreds, ?BASIC_ROMAN_NUMERAL_MAP, "Unmapped100")
    end,

    HundredRem = Int - Thousands - Hundreds,
    TensDiv = HundredRem div 10,
    Tens = TensDiv * 10,
    case TensDiv of
        0 -> TensStr = "";
        _ -> TensStr = maps:get(Tens, ?BASIC_ROMAN_NUMERAL_MAP, "Unmapped10")
    end,

    TensRem = Int - Thousands - Hundreds - Tens,
    OnesStr =  maps:get(TensRem, ?BASIC_ROMAN_NUMERAL_MAP, "Unmapped1"),
    ThousandsStr ++ HundredsStr ++ TensStr ++ OnesStr.
    
test_convert_roman(List) ->
    lists:foreach(fun(Int) -> 
                    Roman = convert_to_roman(Int), 
                    io:format("~p -> ~p~n", [Int, Roman])
                  end, 
                  List).

Howto test:

roman:test_convert_roman([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
                      21, 22, 23, 24, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100,
                      105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 175, 200, 225, 250, 275, 300,
                      325, 350, 375, 400, 425, 450, 475, 500, 525, 540, 550, 575, 600, 625, 650, 675, 700,
                      750, 825, 900, 975, 1000, 1050, 1125, 1200, 1275, 1350, 1425, 1500, 1575, 1650, 1725,
                      1800, 1875, 1950, 2025, 2100, 2175, 2250, 2325, 2400, 2475, 2550, 2700, 3000,
                      3400, 2450, 3500, 3900, 3950, 3999  
]).

Upvotes: 1

Rajat Shenoi
Rajat Shenoi

Reputation: 760

This is a very rigorous question but here is one of the solutions that I personally follow and is easy as well. The answer mentioned here and the answer that I follow are similar but the following is shorter. But I believe the logic is the same.

def printRoman(number):
    num = [1, 4, 5, 9, 10, 40, 50, 90, 
           100, 400, 500, 900, 1000]
    sym = ["I", "IV", "V", "IX", "X", "XL", 
           "L", "XC", "C", "CD", "D", "CM", "M"]
    i = 12
    while number:
        div = number // num[i]
        number %= num[i]
 
        while div:
            print(sym[i], end = "")
            div -= 1
        i -= 1

n = int(input('Enter a number: '))
printRoman(n)

I had learnt about it from here you could refer there as well for detailed explanation on how this works.

Upvotes: 1

Related Questions