我正在尝试编写一个代码,将用户输入的整数转换为其等效的罗马数字.到目前为止我所拥有的是:
该generate_all_of_numeral函数的关键是它为每个特定的数字创建一个字符串.例如,generate_all_of_numeral(2400, 'M', 2000)将返回字符串'MM'.
我正在努力与主程序.我开始找到M的罗马数字计数并将其保存到变量M.然后我减去符号值的M次数,以便为下一个最大数字提供下一个值.
有没有向正确的方向点头?现在我的代码甚至没有打印任何东西.
Jer*_*ino 31
处理此问题的最佳方法之一是使用该divmod功能.检查给定数字是否与从最高到最低的任何罗马数字匹配.在每场比赛中,您都应该返回相应的角色.
使用模数函数时,某些数字会有余数,因此您也将相同的逻辑应用于余数.显然,我暗示递归.
请参阅下面的答案.我使用an OrderedDict来确保我可以向下"迭代"列表,然后我使用递归divmod来生成匹配.最后,我join都生成了生成字符串的答案.
from collections import OrderedDict
def write_roman(num):
roman = OrderedDict()
roman[1000] = "M"
roman[900] = "CM"
roman[500] = "D"
roman[400] = "CD"
roman[100] = "C"
roman[90] = "XC"
roman[50] = "L"
roman[40] = "XL"
roman[10] = "X"
roman[9] = "IX"
roman[5] = "V"
roman[4] = "IV"
roman[1] = "I"
def roman_num(num):
for r in roman.keys():
x, y = divmod(num, r)
yield roman[r] * x
num -= (r * x)
if num <= 0:
break
return "".join([a for a in roman_num(num)])
Run Code Online (Sandbox Code Playgroud)
把它旋转:
num = 35
print write_roman(num)
# XXXV
num = 994
print write_roman(num)
# CMXCIV
num = 1995
print write_roman(num)
# MCMXCV
num = 2015
print write_roman(num)
# MMXV
Run Code Online (Sandbox Code Playgroud)
Azi*_*lto 21
这是另一种方式,没有分裂:
num_map = [(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 num2roman(num):
roman = ''
while num > 0:
for i, r in num_map:
while num >= i:
roman += r
num -= i
return roman
# test
>>> num2roman(2242)
'MMCCXLII'
Run Code Online (Sandbox Code Playgroud)
Ari*_*ide 11
曼哈顿算法的KISS版本,没有任何"高级"概念,如OrderedDict递归,生成器,内部函数和break:
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 int_to_roman(number):
result = ""
for (arabic, roman) in ROMAN:
(factor, number) = divmod(number, arabic)
result += roman * factor
return result
Run Code Online (Sandbox Code Playgroud)
一旦number达到零,就可以添加一个预先输出的退出,并且可以使字符串累积更加pythonic,但我的目标是生成所请求的基本程序.
测试从1到100000的所有整数,这对任何人都应该足够了.
编辑:我提到的稍微更加pythonic和更快的版本:
def int_to_roman(number):
result = []
for (arabic, roman) in ROMAN:
(factor, number) = divmod(number, arabic)
result.append(roman * factor)
if number == 0:
break
return "".join(result)
Run Code Online (Sandbox Code Playgroud)
这是一个用于整数到罗马数字转换的 lambda 函数,最高可达 3999。它锚定了“你可能实际上不想做的不可读的事情”空间的某个角落。但它可能会逗某人:
lambda a: (
"".join(reversed([
"".join([
"IVXLCDM"[int(d)+i*2]
for d in [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
for i,c in enumerate(reversed(str(a))) ]))
)
Run Code Online (Sandbox Code Playgroud)
这种方法提供了使用算术操作来隔离十进制数字及其位置的替代方法,就像 OP 和许多示例一样。这里的方法直接将十进制数转换为字符串。这样,可以通过列表索引来隔离数字。数据表相当压缩,没有使用减法或除法。
诚然,在给定的形式中,任何获得的简洁都会立即放弃可读性。对于没有时间做谜题的人,下面给出了一个避免列表理解和 lambda 函数的版本。
但我将在这里解释 lambda 函数版本......
从后到前:
将十进制整数转换为其数字的反转字符串,并在反转数字 ( c ) 上枚举 ( i )。
....
for i,c in enumerate(reversed(str(a)))
....
Run Code Online (Sandbox Code Playgroud)将每个数字c转换回整数(范围为 0-9),并将其用作魔术数字字符串列表的索引。稍后会解释这个魔法。
....
[ "", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
....
Run Code Online (Sandbox Code Playgroud)将您选择的魔法数字字符串转换为罗马数字“数字”字符串。基本上,您现在将十进制数字表示为适合十进制数字的原始 10 位的罗马数字数字。这是generate_all_of_numeralOP 使用的函数的目标。
....
"".join([
"IVXLCDM"[int(d)+i*2]
for d in <magic digit string>
....
Run Code Online (Sandbox Code Playgroud)以相反的顺序连接所有内容。反转是数字的顺序,但数字内的顺序(“数字”?)不受影响。
lambda a: (
"".join(reversed([
<roman-numeral converted digits>
]))
Run Code Online (Sandbox Code Playgroud)现在,关于魔法字符串列表。它允许为十进制数字可以占据的每个不同的 10 位选择合适的罗马数字字符串(最多四个,每个都是 0、1 或 2 三种类型之一)。
- 0 -> ""; 罗马数字不显示零。
- 1 -> "0"; 0 + 2*i 映射到 I、X、C 或 M -> I、X、C 或 M。
- 2 -> "00"; 比如 1, x2 -> II, XX, CC, MM。
- 3 -> "000"; 比如 1, x3 -> III, XXX, CCC, MMM。
- 4 -> "01"; 像 1,然后 1 +2*i 映射到 V、L 或 D -> IV、XL、CD。
- 5 -> "1"; 映射到奇数罗马数字 -> V、L、D。
- 6 -> "10"; 4 的反转 -> VI、LX、DC。
- 7 -> "100"; 添加另一个 I/X/C -> VII LXX, DCC
- 8 -> "1000"; 添加另一个 I/X/C -> VIII, LXXX, DCCC
- 9 -> "02"; 像 1,加上接下来的 10 级 (2 + i*2) -> IX、XC、CM。
在 4000 及以上,这将引发异常。"MMMM" = 4000,但这不再符合模式,打破了算法的假设。
......正如上面所承诺的......
def int_to_roman(a):
all_roman_digits = []
digit_lookup_table = [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"]
for i,c in enumerate(reversed(str(a))):
roman_digit = ""
for d in digit_lookup_table[int(c)]:
roman_digit += ("IVXLCDM"[int(d)+i*2])
all_roman_digits.append(roman_digit)
return "".join(reversed(all_roman_digits))
Run Code Online (Sandbox Code Playgroud)
我再次遗漏了异常捕获,但至少现在有一个地方可以将它内联。
我参考了这个url进行在线十进制到罗马的转换。如果我们将小数范围扩展到 3,999,999,@Manhattan 给出的脚本将不起作用。以下是 3,999,999 范围内的正确脚本。
def int_to_roman(num):
_values = [
1000000, 900000, 500000, 400000, 100000, 90000, 50000, 40000, 10000, 9000, 5000, 4000, 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
_strings = [
'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
result = ""
decimal = num
while decimal > 0:
for i in range(len(_values)):
if decimal >= _values[i]:
if _values[i] > 1000:
result += u'\u0304'.join(list(_strings[i])) + u'\u0304'
else:
result += _strings[i]
decimal -= _values[i]
break
return result
Run Code Online (Sandbox Code Playgroud)
unicode 字符 u'\0304' 打印上划线字符;例如

示例输出:
Python 包roman(存储库)可用于罗马数字之间的转换:
import roman
r = roman.toRoman(5)
assert r == 'V', r
n = roman.fromRoman('V')
assert n == 5, n
Run Code Online (Sandbox Code Playgroud)
可以使用包管理器从Python 包索引 (PyPI)安装此包: pip
pip install roman
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
51362 次 |
| 最近记录: |