Reputation: 65
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
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
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
Reputation: 689
Here is the solution for Erlang with some notes:
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
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