17

这是 user2486 所说之后的我当前的代码。

def romanMap():
    map=(("M",  1000),("CM", 900),("D",  500),("CD", 400),("C",  100),("XC", 90),("L",  50),("XL", 40),("X",  10),("IX", 9),("V",  5),("V", 4),("I",  1))
    return map
firstNum=ns([0])
secondNum=ns([1])
def main():
    ns=str(input("Enter a roman numeral"))
    total=0
    result=0
    while ns:
        firstNum=(romanMap(ns[0]))
         secondNum=(romanMap(ns[1]) 
        if firstNum is len(ns)>1 or secondNum-1:
                        total=total+firstNum
            ns=ns[1:]
        else:
                        total=total+ns[1]-ns[0]
            ns=ns[2:]
      print (total)
main()

我在 while ns: UnboundLocalError: local variable 'ns' referenced before assignment

4

17 回答 17

29

无需重新发明轮子(除非您愿意)。Python曾经带有一个转换器(因此您可以转到Python 3.4.1源代码并在此位置获取模块:/Python-3.4.1/Doc/tools/roman.py;或者也许像评论中所说的那样使用pip安装它;我还没有验证pip版本; 无论如何,你可以这样做):

import roman;
n=roman.fromRoman("X"); #n becomes 10

如果您需要 5000 及以上的数字,则需要编写一个新函数,并且可能制作自己的字体来表示罗马数字上的线条。(它只适用于某些数字。停在 4999 是一个非常好的主意。)

要转换为罗马数字,请使用roman.toRoman(myInt).

或者(仅用于转换为罗马数字),您可以在 Python 3.9.2 中执行此操作(由于缺乏文档,我只能部分理解;所以,我所有的论点可能都不正确;但是,它似乎有效;格式化程序无论如何都会贬值;所以,不要指望它会停留很长时间):

import formatter
a=formatter.AbstractFormatter("I don't know what I'm supposed to put here, but it doesn't seem to matter for our purposes.")
roman_numeral=a.format_roman(case="I", counter=5) #Case doesn't seem to matter, either.
#roman_numeral now equals "V"

其他人实际上链接到罗马模块在上述评论之一中使用的相同源代码,但我不相信他们提到它实际上是与 Python 一起提供的。它似乎不再随 Python 提供,但在 3.4.1 版本中提供了。

于 2014-04-02T05:34:33.310 回答
10

当您添加或减去每个符号的值时,罗马数字从左到右读取。

如果一个值低于下一个值,它将被减去。否则将被添加。

例如,我们要将罗马数字 MCMLIV 转换为阿拉伯数字:

M = 1000 must be added, because the following letter C =100 is lower.
C = 100 must be subtracted because the following letter M =1000 is greater.
M = 1000 must be added, because the following letter L = 50 is lower.
L = 50 must be added, because the following letter I =1 is lower.
I = 1 must be subtracted, because the following letter V = 5 is greater.
V = 5 must be added, because there are no more symbols left.

我们现在可以计算这个数字:

1000 - 100 + 1000 + 50 - 1 + 5 = 1954 

参考: http: //www.mathinary.com/roman_numerals_from_roman_numerals_to_arabic_numbers.jsp

def from_roman(num):
    roman_numerals = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
    result = 0
    for i,c in enumerate(num):
        if (i+1) == len(num) or roman_numerals[c] >= roman_numerals[num[i+1]]:
            result += roman_numerals[c]
        else:
            result -= roman_numerals[c]
    return result
于 2018-02-01T07:43:49.153 回答
6

考虑这个额外的伪代码和提示(其中一些是有效的 Python,一些不是,但有注释)。

def numberOfNumeral(n):
    """ Return the number represented by the single numeral """
    # e.g. "v" -> 5, "i" -> 5 (and handle v/V cases, etc.)

# avoid "string" as a variable name
# I chose "ns" for "numerals" (which might be better),
# but I'm also a bit terse .. anyway, name variables for what they represents.
ns = str(input("Enter a roman numeral"))

while ns:
   firstNum = numberOfNumeral(ns[0])
   # This makes secondValue = -1 when there is only one numeral left
   # so firstNum is always "at least" secondNum when len(ns) == 1. 
   secondNum = numberOfNumeral(ns[1]) if len(ns) > 1 else -1
   if firstNum is at least secondNum:
      # Add firstNum to total.
      # Remove the character - so that the loop state advances.
      # If we don't don't his, as in the original, it will never end.
      # Here we use "slice notation".
      ns = ns[1:] 
   else:
      # Add the difference, secondNum - firstNum, to total.
      # Remove both characters - again, so we advance state.
      ns = ns[2:]
于 2013-10-11T00:18:08.497 回答
3

好的,您目前拥有的东西有很多问题。

首先,你得到一堆 0 的原因是因为你永远不会退出你的while string != "":循环,它也不会在总数中添加整数。所以total保持为零,并不断被打印。我已经评论了您发布的代码,以帮助您了解正在发生的事情。

def main():
    string=str(input("Enter a roman numeral"))
    total=0
    while string != "": # Empty strings evaluate as False, this can just be 'while string:'
        if string[1] == string[2] or string == len([1]): # Here you are testing the 2nd and 3rd elements.
                                                         # Also, you want to do len(string) == 1
                                                         # string will never == len([1]), so you never
                                                         # execute the code in this block.
            total += string[1]+1   # You want to add the corresponding value of string[0], use a dictionary.
        print (total)

        # Missing the else statement in the pseudocode.
main()

user2864740 在他们发布的解决方案中有一些很好的评论,查看一下,看看你做错了什么。

这是 Python(不幸的是 2.7)代码,它执行您给定的伪代码所说的内容。

val = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}

def main():
    string = str(raw_input('Enter a roman numeral: '))
    string = string.upper()
    total = 0
    while string:
        if len(string) == 1 or val[string[0]] >= val[string[1]]:
            total += val[string[0]]
            string = string[1:]
        else:
            total += val[string[1]] - val[string[0]]
            string = string[2:]
    print total

main()

请注意,您发布的伪代码不正确。请注意它将为 input 做什么'IIV'。它会从 1 中减去 1,然后加 5。但它应该做的是从 5 中减去 2。

于 2013-10-11T00:22:34.250 回答
3

这是我的解决方案:

numerals = [
        {'letter': 'M', 'value': 1000},
        {'letter': 'D', 'value': 500},
        {'letter': 'C', 'value': 100},
        {'letter': 'L', 'value': 50},
        {'letter': 'X', 'value': 10},
        {'letter': 'V', 'value': 5},
        {'letter': 'I', 'value': 1},
    ]

def arabic_to_roman(number):
    remainder = number
    result = ''
    for numeral_index in xrange(len(numerals)):
        numeral = numerals[numeral_index]
        next_numeral = numerals[numeral_index + 1] if numeral_index + 1 < len(numerals) else None

        factor = remainder / numeral['value']
        remainder -= factor * numeral['value']

        if next_numeral:
            numeral_difference = numeral['value'] - next_numeral['value']
            if (remainder - numeral_difference >= 0) and (numeral_difference > next_numeral['value']):
                result += next_numeral['letter'] + numeral['letter']
                remainder -= numeral_difference

        if factor > 0:
            result += numeral['letter'] * factor

    return result


def roman_to_arabic(number):
    index_by_letter = {}
    for index in xrange(len(numerals)):
        index_by_letter[numerals[index]['letter']] = index

    result = 0
    previous_value = None
    for letter in reversed(number):
        index = index_by_letter[letter]
        value = numerals[index]['value']
        if (previous_value is None) or (previous_value <= value):
            result += value
        else:
            result -= value
        previous_value = value

    return result
于 2015-01-03T17:17:45.183 回答
3

一个不错的紧凑版本,没有外部库:

def rn_to_int(s):
  d = {'m': 1000, 'd': 500, 'c': 100, 'l': 50, 'x': 10, 'v': 5, 'i': 1}
  n = [d[i] for i in s.lower() if i in d]
  return sum([i if i>=n[min(j+1, len(n)-1)] else -i for j,i in enumerate(n)])


for numeral, expected in [['CLXIV', 164], ['MDCCLXXXIII', 1783], ['xiv', 14]]:
  assert rn_to_int(numeral) == expected
于 2018-09-20T13:20:11.223 回答
3

从右到左的解决方案有点 Pythonic(没有索引)并且相对较短。

算法:

  • 反转罗马数字并将其映射到数字列表
  • 找出应该减去哪些数字,然后对列表求和

例子:

'xiv'=> sum(5, -1, 10)=>14

def parse_roman(s):
    numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
    n = 0
    last_value = 0
    # e.g. convert 'xiv' to (5, 1, 10)
    for value in (numerals[c] for c in reversed(s.upper())):
        # debugging
        v = (value, -value)[value < last_value]
        print('{:6} += {:5}  <== cur, prev = {}, {}'.format(n, v, value, last_value))
        # subtract smaller values that come after larger ones, otherwise add
        n += (value, -value)[value < last_value]
        last_value = value
    return n

输出:

parse_roman('MMCMXCVIII')
     0 +=     1  <== cur, prev = 1, 0
     1 +=     1  <== cur, prev = 1, 1
     2 +=     1  <== cur, prev = 1, 1
     3 +=     5  <== cur, prev = 5, 1
     8 +=   100  <== cur, prev = 100, 5
   108 +=   -10  <== cur, prev = 10, 100
    98 +=  1000  <== cur, prev = 1000, 10
  1098 +=  -100  <== cur, prev = 100, 1000
   998 +=  1000  <== cur, prev = 1000, 100
  1998 +=  1000  <== cur, prev = 1000, 1000
2998

注意:最好找到一种(短的,内联的)方法来动态更改序列的符号。例如,(5, 1, 10)==> (5, -1, 10)


更新:这与我放弃之前的情况一样接近。它与上面的代码相同,但它使用itertools.tee()withzip()生成先前值和当前值对,以消除对状态变量的需要。单次调用next(cur)使该列表比prev我们需要确定是否添加或减去当前值的所有状态短一个。

例子:

cur, prev = (5, 1, 10), (5, 1, 10)
# Take one from cur and zip the rest
next(cur) + sum(... zip(cur, prev))
# 5 + ... zip( (1, 10), (5, 1, 10) )  ==>  5 + ... ((1, 5), (10, 1)) 

代码:

from itertools import tee

def parse_roman(s):
    numerals = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1}
    cur, prev = tee(numerals[c] for c in reversed(s.upper()))
    return next(cur) + sum((cur, -cur)[cur < prev] for cur, prev in zip(cur,prev))
于 2017-03-28T01:45:31.347 回答
2

我知道这是一篇旧帖子,但我想添加 3 个解决方案来将罗马数字转换为数字。

解决方案 1:(大约运行时间 = 52 毫秒)

def romanToInt(self, s: str) -> int:

     roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }    
     num = 0

     for i in range(len(s)):

        if i!= len(s)-1 and roman[s[i]] < roman[s[i+1]]:
             num += roman[s[i]]*-1
        else:
             num += roman[s[i]]

      return num

解决方案 2:(大约运行时间 = 60 毫秒)

def romanToInt(self, s: str) -> int:

     roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }    
     num = 0

     s = s.replace("IV", "IIII").replace("IX", "VIIII")
     s = s.replace("XL", "XXXX").replace("XC", "LXXXX")
     s = s.replace("CD", "CCCC").replace("CM", "DCCCC")

     for x in s:
        num += roman[x]

     return num

解决方案 3:(大约运行时间 = 48 毫秒)

def romanToInt(self, s: str) -> int:

     roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000 }    
     num = 0

     for i in range(len(s)-1):
        if roman[s[i]] < roman[s[i+1]]:
            num += roman[s[i]]*-1
            continue

         num += roman[s[i]]

      num +=roman[s[-1]]

      return num

最简单的解决方案有时似乎是最好的:)

于 2020-06-09T13:57:00.953 回答
1

这段代码呢

mapping = {'I': 1, 'V': 5, 'X': 10,'L': 50, 'C': 100, 'D': 500, 'M':1000}

def roman_to_dec(roman):
"""
Convert the roman no to decimal
"""
dec = last = 0
for i in range(0, len(roman)):
    no = mapping.get(roman[i])
    # subtract last 2 times cuz one for this pass and another for last pass
    dec = dec + (no - 2 * last) if no > last else dec + no
    last = no
return dec
于 2017-01-05T11:31:13.637 回答
0

这是我使用字典想出的东西。它应该很简单。告诉我你的想法。我必须说它不处理以 MIM 形式编写的恶搞罗马数字(而不是 1999 年的 MCMXCIX)。这仅适用于有效的罗马数字。

import re
s = 0;
a = dict();
b = dict();
r = "MMCMXCVIII"

a['CM'] = 900;
a['IX'] = 9;
a ['IV'] = 4;
a ['XL'] = 40;
a ['CD'] = 400;
a ['XC'] = 90;

b['M'] = 1000;
b['C'] = 100;
b['D'] = 500;
b['X'] = 10;
b['V'] = 5;
b['L'] = 50;
b['I'] = 1;

# Handle the tricky 4's and 9's first and remove them from the string

for key in a:
        if key in r: 
            r = re.sub(key,'',r)
            s+=a[key];
# Then straightforward multiplication of the not-so-tricky ones by their count.

for key in b:
         s+= r.count(key) * b[key];

print s; # This will print 2998
于 2015-01-19T17:16:01.333 回答
0

Mark Pilgrim在Dive Into Python 3中对罗马数字转换器的开发进行了非常详细的描述。见5.3。案例研究:介绍问题和细节的罗马数字。

但这还不是全部。请参阅第 9 章。单元测试继续分析和实现罗马数字转换器,包括有趣的优化和异常抛出——(单元)测试驱动的开发。

它与 enginefree 对问题下方第一条评论中代码的引用直接相关(代码由 Mark Pilgrim 编写)。

于 2013-10-11T07:59:36.057 回答
0

这是 leetcode 的问题之一。

def romanToInt(s):
    sum=0
    dict={'M':1000,'D':500,'C':100,'L':50,'X':10,'V':5,'I':1}

    for i in range(len(s)):
        if i==0:
            sum=sum+dict[s[i]]
        else:
            if s[i]=='M':
                sum=sum+1000
                if s[i-1]=='C':
                    sum=sum-200      

            elif s[i]=='D':
                sum=sum+500
                if s[i-1]=='C':
                    sum=sum-200

            elif s[i]=='C':
                sum=sum+100
                if s[i-1]=='X':
                    sum=sum-20

            elif s[i]=='L':
                sum=sum+50
                if s[i-1]=='X':
                    sum=sum-20

            elif s[i]=='X':
                sum=sum+10
                if s[i-1]=='I':
                    sum=sum-2

            elif s[i]=='V':
                sum=sum+5
                if s[i-1]=='I':
                    sum=sum-2
            elif s[i]=='I':
                sum=sum+1
    return (sum)
于 2018-04-30T18:46:50.647 回答
0

您可以使用以下代码:

def roman_integer(roman):
    roman = roman.upper() # for taking care of upper or lower case letters
    integer_rep = 0
    roman_to_integer_map = tuple()
    roman_to_integer_map = (('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))
    roman_numeral_pattern = re.compile("""
    ^                   # beginning of string
    M{0,4}              # thousands - 0 to 4 M's
    (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
                        #            or 500-800 (D, followed by 0 to 3 C's)
    (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
                        #        or 50-80 (L, followed by 0 to 3 X's)
    (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
                        #        or 5-8 (V, followed by 0 to 3 I's)
    $                   # end of string
    """ ,re.VERBOSE)

    if not roman_numeral_pattern.search(roman):
        return 0
    index = 0
    for numeral, integer in roman_to_integer_map:
        while roman[index:index+len(numeral)] == numeral:
            #print numeral, integer, 'matched'
            integer_rep += integer
            index += len(numeral)
    return integer_rep

于 2016-02-22T11:07:45.313 回答
0
def romanToInt(self, s: str) -> int:
    roman_dict = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
    int_equ = 0

    for i in range(len(s)):
        if i > 0 and roman_dict[s[i]] > roman_dict[s[i-1]]:
            int_equ += roman_dict[s[i]] - 2*roman_dict[s[i-1]]
        else:
            int_equ += roman_dict[s[i]]

    return int_equ
于 2020-02-17T08:15:15.007 回答
0

从罗马数字的右到左工作以增加或减少值。简单的。

def rome(roman_num):
     d = {'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
     nl = list(roman_num)
     sum = d[nl[len(nl)-1]]
     for i in range(len(nl)-1,0,-1):
             if d[nl[i]]>d[nl[i-1]]:
                     sum -= d[nl[i-1]]
             else:
                     sum += d[nl[i-1]]       
     return sum
于 2017-03-23T23:32:25.493 回答
0
roman_conver=[  (1,'I'),
                (5,'V'),
                (10,'X'),
                (50,'L'),
                (100,'C'),
                (500,'D'),
                (1000,'M'),
                    ]
def romantonumeral(roman):
    tot = 0
    for i in range(0,len(roman)):
        for each in roman_conver:
            if roman[i]==each[1]:
                if each[0]>tot:
                    tot = each[0] - tot
                else:
                    tot = tot + each[0]
    return tot
于 2017-07-31T10:41:27.350 回答
-3

尝试这个:

def translate(string):
    values = {"i":1, "v":5, "x":10, "l":50, "c":100, "m":1000}
    return sum(map(lambda x: values[x], string))

lambda 代表单行函数。这就是为什么它们被称为匿名函数。您不必在外面使用 def 和所有这些形式来定义它们。

您可以在 shell 上键入类似这样的内容:

f = lambda x: x + 3 f(3) 6 或 f = lambda x,y: x + y f("foo", "bar") 'foobar'

我使用map将我的新功能应用到可迭代的每个元素中。在这种情况下,可迭代对象是一个字符串,例如“mclvii”。这样做我生成了一个列表,其中每个值都是其各自的值。请参阅 lambda 示例来计算平方:

>>> a = [1,2,3,4]
>>> square = lambda x: x**2
>>> l = map(square, a)
>>> l = [1,4,9,16]

因此,当您需要动态函数时,它是 lambda,当您想要将函数应用于列表中的所有元素时,它是 map。

现在一个使用递归的例子:

def translate2(string):
    values = {"i":1, "v":5, "x":10, "l":50, "c":100, "m":1000}
    if not string:
        return 0
    return values[string[0]] + translate2(string[1:])
于 2013-10-11T00:42:42.850 回答