JagaSrik
JagaSrik

Reputation: 718

how to code in python with the following logic for the roman numeral to integer conversion

I am trying to convert the given roman numerals to just numerals for programming practice, with the following logic (I dont want to change this logic unless its falsely thought of) Here,

M - 1000, C-100, X-10, V-5, I-1

example :

Input - MCMXCVI

Expected Result - 1996

logic - 1000 + (1000-100) + (100-10) + 5 + 1

index- 1 + (3-2) + (5-4) + 6 + 7

Here i am searching next value from the current value subtracting it if its not greater we are adding it normally.

Here is what i have tried, i could't code it correctly, having spent lot of time, thought to ask out for help.

def roman_numeral(num):
    """
    Write a Python class to convert an roman numeral to a integer.
    Logic: https://www.rapidtables.com/convert/number/how-roman-numerals-to-number.html
    """
    # Input the string 
    # Map of roman numerals and the corresponding values in a dictionary.
    NUMERALS = {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'}
    retval=[]
     
    #Check if each char matches with the dictionary and take the numerical value of the inputed roman 
     
    for k in range(len(num)):
        for i,j in NUMERALS.items():
            if(j==num[k]):
                retval.append(i)
                 
    elm_count = len(retval)       
    result=0 
    result_less=0
    result_more=0
    ind_tracker=0
     
#Check if next char from the position of current char if that numerical value is greater then current numerical value.
#If it is greater subtract the current numeric value, if not greater then add it.    
    for ind,i in enumerate(retval):
        print('ind= ',ind,'i= ', i)
#Using this below condition to skip if we have already subtracted the current value from previous value.
        if( ind_tracker>ind):
            continue
        if((ind+1 < elm_count)):
                if(i<retval[ind+1]):
                    #print('result=',result,'retval[ind]=',retval[ind],'retval[ind+1]=', retval[ind+1])
                    result_less=retval[ind+1]-retval[ind]
                    print('result_less=',result_less)
                    ind_tracker=ind+1
                else:
                    result_more+=retval[ind]+result_less
                    print('result_more=',result_more)
                     
                    result=result_more   
    print('final result= ',result)    
    return result
 
roman_numeral('MCMXCVI')

The output im getting is

3185 

I expect to get

1996

Upvotes: 0

Views: 578

Answers (3)

I made some minor changes to your existing code!

  1. I added a variable "flag" set to False on default and when result_less, I set it to true
  2. To check we subtracted or not, I used this flag, when the flag is true, i made it false and skipped an iternation.
  3. added a new if statement to check for last number
  4. on result_more, result+=retval[ind], did not used result_less value
  5. on result_less, result+=retval[ind+1]-retval[ind]. in both cases i changed the result value rather than changing more and less values for simplicity.

and BTW, I got rid of those result_more and result_less variables but kept the print statements.

here is your code, modified:

def roman_numeral(num):
    """
    Write a Python class to convert an roman numeral to a integer.
    Logic: https://www.rapidtables.com/convert/number/how-roman-numerals-to-number.html
    """
    # Input the string 
    # Map of roman numerals and the corresponding values in a dictionary.
    NUMERALS = {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'}
    retval=[]
     
    #Check if each char matches with the dictionary and take the numerical value of the inputed roman 
     
    for k in range(len(num)):
        for i,j in NUMERALS.items():
            if(j==num[k]):
                retval.append(i)

    elm_count = len(retval)       
    result=0 
    result_less=0
    result_more=0
    # ind_tracker=0
    flag = False
    
    #Check if next char from the position of current char if that numerical value is greater then current numerical value.
    #If it is greater subtract the current numeric value, if not greater then add it.   

    for ind,i in enumerate(retval):
        print('ind= ',ind,'i= ', i)
        #Using this below condition to skip if we have already subtracted the current value from previous value.
        # if( ind_tracker>ind):
        #     continue
        
        if(flag):
          print("Skipped! Already Subracted!")
          flag=False
          continue
        
        if((ind+1 == elm_count)):
          # if last digit is greater than it's previous, the flag will skip this iteration
          print('last digit=',retval[ind])   
           result+=retval[ind]
        
        if((ind+1 < elm_count)):
                if(i<retval[ind+1]):
                    #print('result=',result,'retval[ind]=',retval[ind],'retval[ind+1]=', retval[ind+1])
                    # result_less=retval[ind+1]-retval[ind]
                    result+=retval[ind+1]-retval[ind]
                    print('result_less=',retval[ind+1]-retval[ind])
                    # ind_tracker=ind+1
                    flag = True
                else:
                    # result_more+=retval[ind]+result_less
                    result+=retval[ind]
                    print('result_more=',retval[ind])            
                    # result=result_more   
    
    print('final result= ',result)    
    return result

roman_numeral('MCMXCVI')

Upvotes: 1

Jan
Jan

Reputation: 43169

You could use (self-implemented):

class RomanToDecimal:
    conversion = {'M': 1000, 'CM': 900, 'D': 500, 'CD': 400, 'C': 100, 'XC': 90, 'L': 50, 'XL': 40, 'X': 10, 'IX': 9,
                  'V': 5, 'IV': 4, 'I': 1}

    def convert(self, roman):
        total = 0
        while len(roman):
            before = len(roman)
            for key in self.conversion:
                if roman.startswith(key):
                    total += self.conversion[key]
                    roman = roman[len(key):]
            after = len(roman)
            if before == after:
                raise ValueError("Not a Roman numeral.")
        return total

try:
    rtd = RomanToDecimal()
    assert rtd.convert('M') == 1000
    assert rtd.convert('XXXVI') == 36
    assert rtd.convert('MMXII') == 2012
    assert rtd.convert('MMXX') == 2020
except ValueError as error:
    print(error)

Upvotes: 2

Andreas
Andreas

Reputation: 9207

You can change the basic concept. If you reverse the roman numbers and basically start from the right side of the string the whole thing get really simple.

The idea is that if you start from the right, if the next number is bigger or equal to the current number you add the number to the total, if the next number is smaller than the previous one then it is substraced from the total.

roman = "MCMXCVI"

NUMERALS = {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'}

# reverse roman number letters (basically start from the end
roman_reversed = list(reversed(roman))
#invert the dictionary because we want to translate the letter to numbers not the other way around
inverse_NUMERALS = {v: k for k, v in NUMERALS.items()}

# get the number for each character on its own:
lst_numbers = [inverse_NUMERALS.get(x) for x in roman_reversed]


# loop through the list of numbers
total = 0
previous = 0
for numb in lst_numbers:
    if numb >= previous:
        total += numb
    else:
        total -= numb
    previous = numb
    
print(total)
#Out[21]: 1996

Upvotes: 2

Related Questions