FreedomLy's blog.

【每周一坑】罗马数字转换

字数统计: 1.1k阅读时长: 5 min
2017/07/08 Share

roman

罗马数字是欧洲在阿拉伯数字传入之前使用的一种数码,现在的使用已经非常少了,大概偶尔会在钟表、文章中的标号等地方还能见到。

罗马数字采用七个罗马字母做数字,即I(1), V(5), X(10), L(50), C(100), D(500), M(1000)。它有一套不同于阿拉伯数字的写法规则,简单来说可以总结为:

  1. 相同的数字连写,所表示的数等于这些数字相加得到的数,如 Ⅲ= 3
  2. 小的数字在大的数字的右边,表示的数等于这些数字相加得到的数,如 Ⅷ=8, Ⅻ=12
  3. 小的数字(限于Ⅰ、Ⅹ、C)在大的数字的左边,所表示的数等于大数减小数得到的数,如Ⅳ=4、Ⅸ=9
  4. 在一个数的上面画一条横线,表示这个数曾1000倍

常见罗马字符表

数字 罗马字符 数字 罗马字符
1 2
3 4
5 6
7 8
9 10
40 XL 50 L
90 XC 100 C
400 CD 500 D
900 CM 1000 M
2000 MM 2500 MMD

问题

编写一个罗马数字和阿拉伯数字的转换器:

给定一个小于3999的罗马数,将其转换为整数,例如:Ⅲ=3、Ⅳ=4、Ⅵ=6、XIX=19、XX=20、XLV=45、MCMLXXX=1980

附加题

给定一个小于3999的整数,将其转换为罗马数

示例:

1
2
3
4
5
6
7
8
9
10
11
def romanToInt(s):
# your code
return i

assert romanToInt('III') == 3
assert romanToInt('IV') == 4
assert romanToInt('VI') == 6
assert romanToInt('XIX') == 19
assert romanToInt('XX') == 20
assert romaToInt('XLV') == 45
assert romanToInt('MCMLXXX') == 1980

思路

罗马数转阿拉伯数

根据罗马数的规则,要把罗马数转换为阿拉伯数只需把每位罗马数加起来即可,不过要注意IV、IX、XL、XC、CD、MC这些表示(5-1、10-1、50-10、100-10、500-100、1000-100)的情况。故在计算阿拉伯数时要判断是否是上述的数。观察可知以上数中,左边的罗马数均比右边的小,故可以以这个为判断条件,来确定是进行加操作还是减操作。可以将罗马数字符反转,然后从头遍历,如果当前罗马数小于上一位,则减去当前的罗马数所对应的数字;否则,加上当前罗马数所对应的数字

如 MCMLXXX,首先将其反转,得到XXXLMCM,再开始遍历(res表示结果,pre表示上一位罗马数):

  1. 第一位是X,表示10,pre = 0,res = 0 + 10
  2. 第二位是X,表示10,pre = 10,与Pre相等,则res = 10 + 10
  3. 第三位是X,表示10,pre = 10,与pre相等,则res = 20 + 10
  4. 第四位是L,表示50,pre = 10,大于pre,则res = 30 + 50
  5. 第五位是M,表示1000,pre = 50, 大于pre,则res = 80 + 1000
  6. 第六位是C,表示100, pre = 1000, 小于pre,则res = 1080 - 100
  7. 第七位是M,表示1000, pre = 100, 大于pre,则res = 980 + 1000

经过上述步骤,得到MCMLXXX对应的阿拉伯数为1980,结果正确

阿拉伯数(整数)转罗马数

将输入的整数不断除罗马数中的几个关键数(I、IV、V、IX、X、XL、CX、CD、D、CM、M)然后将由除法得到的数(即表示改罗马数的个数)加到结果中,再对整数取余,直到整数取余的结果为0

如1980,(res表示结果):

  1. 1980 // 1000 = 1, res += M, 1980 % 1000 = 980
  2. 980 // 900 = 1, res += CM, 980 % 900 = 80
  3. 80 // 50 = 1, res += L, 80 % 50 = 30
  4. 30 // 10 = 3, res += 3*X, 30 % 10 = 0

经过上述步骤即可得整数1980 = MCMLXXX

Python实现

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# -*- coding: utf-8 -*-
# 罗马数字转换
# 1 I 4 IV 5V 9 IX 10X 40XL 50L 70 LXX 90XC 100C 200CC
# 400CCC;CD 500D 900CM 1000M 2500MMD

# e.g.
# 3->III
# 4->IV
# 6->VI
# 19->XIX
# 45->XLV
# 999->CMXCIX
# 1980->MCMLXXX
# 3999->MMMCMXCIX

# 罗马转数字
def roman_to_int(s):
roman_int_dic = {
'I': 1, 'V': 5, 'X': 10,
'L': 50, 'C': 100, 'D': 500,
'M': 1000
}
s = s[::-1]
res, pre = 0, 0
for x in s:
if roman_int_dic[x] >= pre:
res += roman_int_dic[x]
pre = roman_int_dic[x]
else:
res -= roman_int_dic[x]
pre = roman_int_dic[x]
return res


# 数字转罗马
def int_to_roman(i):
dic = {
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'
}
res = ""
for k in dic:
if i != 0:
res += i // k * dic[k]
i %= k
return res


# 测试
if __name__ == '__main__':
result = roman_to_int('MMMCMXCIX')
print(result)

int_res = int_to_roman(3999)
print(int_res)

result = roman_to_int('MCMLXXX')
print(result)

int_res = int_to_roman(1980)
print(int_res)

assert roman_to_int('III') == 3
assert roman_to_int('IV') == 4
assert roman_to_int('VI') == 6
assert roman_to_int('XIX') == 19
assert roman_to_int('XLV') == 45
assert roman_to_int('MCMLXXX') == 1980
assert roman_to_int('CMXCIX') == 999
print("OK")

测试

输出结果

1
2
3
4
5
6
7
8
9
10
11
>>> roman_to_int('MMMCMXCIX')
3999

>>> int_to_roman(3999)
MMMCMXCIX

>>> roman_to_int(MCMLXXX)
1980

>>> int_to_roman(1980)
MCMLXXX

结果与要求一致

End~


CATALOG
  1. 1. 问题
  2. 2. 思路
    1. 2.1. 罗马数转阿拉伯数
    2. 2.2. 阿拉伯数(整数)转罗马数
  3. 3. Python实现
  4. 4. 测试