ユーザーが入力した整数をローマ数字に変換するコードを書こうとしています。これまでのところ:
_generate_all_of_numeral
_関数のポイントは、特定の数値ごとに文字列を作成することです。たとえば、generate_all_of_numeral(2400, 'M', 2000)
は、文字列_'MM'
_を返します。
メインプログラムで苦労しています。まず、Mのローマ数字のカウントを見つけて、変数Mに保存します。次に、Mの数にシンボル値を掛けた値を差し引いて、次に大きい数字を処理するための次の値を取得します。
正しい方向にうなずきますか?現在、私のコードは何も出力しません。
これに対処する最良の方法の1つは、divmod
関数を使用することです。与えられた数値が、最高から最低までのローマ数字と一致するかどうかを確認します。すべての一致で、それぞれの文字を返す必要があります。
モジュロ関数を使用すると、一部の数値に余りがあるため、同じロジックを余りにも適用します。明らかに、私は再帰をほのめかしています。
以下の私の答えを参照してください。 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)])
試してみる:
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
分割のない別の方法を次に示します。
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'
A 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
number
がゼロになり次第、成熟前の出口を追加し、文字列の累積をよりPythonicにすることができますが、ここでの目標は、要求されたbasicプログラムを作成することでした。
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)
オンラインの10進数からローマ数字への変換について、これを参照しました rl 。小数の範囲を最大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', 'C', '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
Unicode文字u '\ 0304'は上線文字を出力します。例えば
出力例:
整数からローマ数字への変換用のラムダ関数を次に示します。最大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))) ]))
)
このアプローチは、OPや多くの例のように、算術操作を使用して10進数とその場所を分離する代替手段を提供します。ここでのアプローチは、10進数を文字列に変換するためのストレートです。このようにして、リストの索引付けによって数字を分離できます。データテーブルはかなり圧縮されており、減算や除算は使用されません。
確かに、与えられた形式では、簡潔に得られたものはすべてすぐに読みやすさが失われます。パズルの時間がない人のために、リスト内包表記とラムダ関数を回避する以下のバージョンが与えられています。
しかし、ここではラムダ関数のバージョンについて説明します...
後ろから前へ:
10進整数をその数字の逆の文字列に変換し、逆の数字(c)を列挙(i)します。
....
for i,c in enumerate(reversed(str(a)))
....
各数字cを整数(0〜9の範囲)に戻し、それをマジックディジットストリングのリストへのインデックスとして使用します。魔法は少し後で説明されます。
....
[ "", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
....
選択した魔法の数字の文字列をローマ数字の「数字」の文字列に変換します。基本的に、10進数は10進数の元の10の位に適切なローマ数字として表されます。これは、OPが使用するgenerate_all_of_numeral
関数のターゲットです。
....
"".join([
"IVXLCDM"[int(d)+i*2]
for d in <magic digit string>
....
すべてを逆の順序で連結します。逆順は数字の順番ですが、順番within数字( "数字"?)は影響を受けません。
lambda a: (
"".join(reversed([
<roman-numeral converted digits>
]))
さて、その魔法の文字列のリストについて。これにより、10進数字が占める10桁ごとに、ローマ数字の適切なストリング(最大4つ、それぞれが3つのタイプ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))
再び例外トラップを省略しましたが、少なくとも今はインライン化する場所があります。
笑う男によるアプローチが機能します。順序付き辞書の使用は賢いです。しかし、彼のコードは、関数が呼び出されるたびに順序付きディクショナリを再作成し、関数内では、すべての再帰呼び出しで、関数は順序付きディクショナリ全体を上からステップ実行します。また、divmodは商と剰余の両方を返しますが、剰余は使用されません。より直接的なアプローチは次のとおりです。
def _getRomanDictOrdered():
#
from collections import OrderedDict
#
dIntRoman = OrderedDict()
#
dIntRoman[1000] = "M"
dIntRoman[900] = "CM"
dIntRoman[500] = "D"
dIntRoman[400] = "CD"
dIntRoman[100] = "C"
dIntRoman[90] = "XC"
dIntRoman[50] = "L"
dIntRoman[40] = "XL"
dIntRoman[10] = "X"
dIntRoman[9] = "IX"
dIntRoman[5] = "V"
dIntRoman[4] = "IV"
dIntRoman[1] = "I"
#
return dIntRoman
_dIntRomanOrder = _getRomanDictOrdered() # called once on import
def getRomanNumeralOffInt( iNum ):
#
lRomanNumerals = []
#
for iKey in _dIntRomanOrder:
#
if iKey > iNum: continue
#
iQuotient = iNum // iKey
#
if not iQuotient: continue
#
lRomanNumerals.append( _dIntRomanOrder[ iKey ] * iQuotient )
#
iNum -= ( iKey * iQuotient )
#
if not iNum: break
#
#
return ''.join( lRomanNumerals )
結果の確認:
>>> getRomanNumeralOffInt(35)
'XXXV'
>>> getRomanNumeralOffInt(994)
'CMXCIV'
>>> getRomanNumeralOffInt(1995)
'MCMXCV'
>>> getRomanNumeralOffInt(2015)
'MMXV'
ほとんどの回答で、人々は"IX" for 9
、"XL" for 40
などのような過剰な表記を格納していることを確認しました。
これはローマの回心の主要な本質を見逃しています。
実際にコードを貼り付ける前の簡単な紹介とアルゴリズムを次に示します。
ローマ数字の元のパターンは、記号I、V、およびX(1、5、および10)を単純なタリーマークとして使用していました。 1(I)の各マーカーは最大5(V)までの単位値を追加し、次に(V)に追加して6から9までの数値を作成します。
I、II、III、IIII、V、VI、VII、VIII、VIIII、X。
4(IIII)と9(VIIII)の数字には問題があり、通常IV(5未満)とIX(1未満)に置き換えられます。このローマ数字の特徴は、減算表記法と呼ばれます。
1から10までの数字(4と9の減算表記を含む)は、次のようにローマ数字で表されます。
I、II、III、IV、V、VI、VII、VIII、IX、X。
システムは基本的に10進数であり、数十と数百は同じパターンに従います。したがって、10から100を数えます(10を数え、XがIの代わりにL、CがVの代わりになり、CがXの代わりになります)。
X、XX、XXX、XL、L、LX、LXX、LXXX、XC、C。 ローマ数字-ウィキペディア
したがって、上記の概要から派生できる主なロジックは、位置の値を取り除き、ローマ字が使用するリテラルの値に基づいて除算を実行することです。
基本的な例から始めましょう。 [10, 5, 1]
としてリテラルの統合リストがあります
1/10 = 0.1(あまり役に立たない)
1/5 = 0.2(あまり役に立たない)
1/1 = 1.0(うーん、何かを得ました!)
CASE 1:したがって、商== 1の場合、整数に対応するリテラルを出力します。したがって、最良のデータ構造は辞書になります。 {10: "X", 5: "V", 1:"I"}
「私」が印刷されます。
2/10 = 0.2
2/5 = 0.4
2/1 = 2
CASE 2:したがって、商> 1の場合、それを作成した整数に対応するリテラルを出力し、それを数値から減算します。これは1になり、CASE 1に該当します。「II」が印刷されます。
3/10 = 0.3
3/5 = 0.6
3/1 = 3
したがって、CASE 2: "I"、CASE 2: "II"、CASE 1: "III"
ケース3:1を追加し、商== 1かどうかを確認します。
(4 + 1)/ 10 = 0.5
(4 + 1)/ 5 = 1
したがって、これは、最初に除数と数値を減算し、結果に対応するリテラルを出力し、その後に除数を出力する場合です。 5-4 = 1なので、「IV」が印刷されます。
(9 + 1)/ 10 == 1
10-9 = 1。 「I」を印刷、「X」を印刷、つまり「IX」
これは10の位および100の位にも拡張されます。
(90+(10 ^ 1))/ 100 = 1。
100-90 = "X"の後に100 = "C"を印刷します。
(400+(10 ^ 2))/ 500 = 1。
500-400 = "C"の後に500 = "D"を印刷します。
ここで最後に必要なのは、位置の値を抽出することです。例:449は400、40、9を生成するはずです。
これは、10 ^(position-1)のモジュロを減算してから、10 ^ positionのモジュロを取ることで作成できます。
例:449、位置= 2:449%(10 ^ 1)= 9-> 449-9-> 440%(10 ^ 2)= 40。
'''
Created on Nov 20, 2017
@author: lu5er
'''
n = int(input())
ls = [1000, 500, 100, 50, 10, 5, 1]
st = {1000:"M", 500:"D", 100:"C", 50:"L", 10:"X", 5:"V", 1:"I"}
rem = 0
# We traverse the number from right to left, extracting the position
for i in range(len(str(n)), 0, -1):
pos = i # stores the current position
num = (n-n%(10**(pos-1)))%(10**pos) # extracts the positional values
while(num>0):
for div in ls:
# CASE 1: Logic for 1, 5 and 10
if num/div == 1:
#print("here")
print(st[div], end="")
num-=div
break
# CASE 2: logic for 2, 3, 6 and 8
if num/div > 1:
print(st[div],end="")
num-=div
break
# CASE 3: Logic for 4 and 9
if (num+(10**(pos-1)))/div == 1:
print(st[div-num], end="")
print(st[div], end="")
num-=div
break
出力テスト
99
XCIX
499
CDXCIX
1954
MCMLIV
1990
MCMXC
2014
MMXIV
35
XXXV
994
CMXCIV
これを行う別の方法。 4、9などで始まる番号の処理を分離します。さらに簡略化できます
def checkio(data):
romans = [("I",1),("V",5),("X",10),("L",50),("C",100),("D",500),("M",1000)]
romans_rev = list(sorted(romans,key = lambda x: -x[1]))
def process_9(num,roman_str):
for (k,v) in romans:
if (v > num):
current_roman = romans[romans.index((k,v))]
prev_roman = romans[romans.index((k,v)) - 2]
roman_str += (prev_roman[0] + current_roman[0])
num -= (current_roman[1] - prev_roman[1])
break
return num,roman_str
def process_4(num,roman_str):
for (k,v) in romans:
if (v > num):
current_roman = romans[romans.index((k,v))]
prev_roman = romans[romans.index((k,v)) - 1]
roman_str += (prev_roman[0] + current_roman[0])
num -= (current_roman[1] - prev_roman[1])
break
return num,roman_str
def process_other(num,roman_str):
for (k,v) in romans_rev:
div = num // v
if ( div != 0 and num > 0 ):
roman_str += k * div
num -= v * div
break
return num,roman_str
def get_roman(num):
final_roman_str = ""
while (num > 0):
if (str(num).startswith('4')):
num,final_roman_str = process_4(num,final_roman_str)
Elif(str(num).startswith('9')):
num,final_roman_str = process_9(num,final_roman_str)
else:
num,final_roman_str = process_other(num,final_roman_str)
return final_roman_str
return get_roman(data)
print(checkio(number))
私はこの変換をカタの練習として進めていて、Pythonの文字列操作を利用するソリューションを思いつきました。
from collections import namedtuple
Abbreviation = namedtuple('Abbreviation', 'long short')
abbreviations = [
Abbreviation('I' * 1000, 'M'),
Abbreviation('I' * 500, 'D'),
Abbreviation('I' * 100, 'C'),
Abbreviation('I' * 50, 'L'),
Abbreviation('I' * 10, 'X'),
Abbreviation('I' * 5, 'V'),
Abbreviation('DCCCC', 'CM'),
Abbreviation('CCCC', 'CD'),
Abbreviation('LXXXX', 'XC'),
Abbreviation('XXXX', 'XL'),
Abbreviation('VIIII', 'IX'),
Abbreviation('IIII', 'IV')
]
def to_roman(arabic):
roman = 'I' * arabic
for abbr in abbreviations:
roman = roman.replace(abbr.long, abbr.short)
return roman
私はそのシンプルさが好きです!モジュロ演算、条件、または複数のループは必要ありません。もちろん、namedtuple
sも必要ありません。代わりにプレーンタプルまたはリストを使用できます。
roman_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 IntToRoman (xn):
x = xn
y = 0
Str = ""
for i, r in roman_map:
# take the number and divisible by the roman number from 1000 to 1.
y = x//i
for j in range(0, y):
# If after divisibility is not 0 then take the roman number from list into String.
Str = Str+r
# Take the remainder to next round.
x = x%i
print(Str)
return Str
テストケース:
>>> IntToRoman(3251)
MMMCCLI
'MMMCCLI'
これは私の再帰的なアプローチです
def itr(num):
if(num == 1): return "I"
if(num == 4): return "IV"
if(num == 5): return "V"
if(num == 9): return "IX"
if(num == 10): return "X"
if(num == 40): return "XL"
if(num == 50): return "L"
if(num == 90): return "XC"
if(num == 100): return "C"
if(num == 400): return "CD"
if(num == 500): return "D"
if(num == 900): return "CM"
if(num == 1000): return "M"
for i in [1000, 100, 10, 1]:
for j in [9*i, 5*i, 4*i, i]:
if(num>=j):
return itr(j) + itr(num-j)
def test(num):
try:
if type(num) != type(1):
raise Exception("expected integer, got %s" % type(num))
if not 0 < num < 4000:
raise Exception("Argument must be between 1 and 3999")
ints = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
nums = ('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I')
result = ""
for i in range(len(ints)):
count = int(num / ints[i])
result += nums[i] * count
num -= ints[i] * count
print result
except Exception as e:
print e.message
while True:
num = input()
def val(n):
if n == 1:
rom = 'I'
return rom
if n == 4:
rom = 'IV'
return rom
if n == 5:
rom = 'V'
return rom
if n == 9:
rom = 'IX'
return rom
if n == 10:
rom = 'X'
return rom
if n == 40:
rom = 'XL'
return rom
if n == 50:
rom = 'L'
return rom
if n == 90:
rom = 'XC'
return rom
if n == 100:
rom = 'C'
return rom
if n == 400:
rom = 'CD'
return rom
if n == 500:
rom = 'D'
return rom
if n == 900:
rom = 'CM'
return rom
def lastdigit(num02):
num02 = num % 10
num03 = num % 5
if 9 > num02 > 5:
return str('V' + 'I'*num03)
Elif num02 < 4:
return str('I'*num03)
else:
return str(val(num02))
k3 = lastdigit(num)
def tensdigit(num12):
num12 = num % 100 - num % 10
num13 = num % 50
if 90 > num12 > 50:
return str('L' + 'X'*(num13/10))
Elif num12 < 40:
return str('X'*(num13/10))
else:
return str(val(num12))
k2 = tensdigit(num)
def hundigit(num112):
num112 = (num % 1000 - num % 100)
num113 = num % 500
if 900 > num112 > 500:
return str('D' + 'C'*(num113/100))
Elif num112 < 400:
return str('C'*(num113/100))
else:
return str(val(num112))
k1 = hundigit(num)
print '%s%s%s' %(k1,k2,k3)
SymbolCountをグローバル変数にする必要があります。そして、()をprintメソッドで使用します。
このローマ数字のコードは、間違った文字のようなエラーをチェックしません。完全なローマ数字の文字のためだけです
roman_dict = {'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 = input('Enter the roman numeral: ').upper()
roman_initial = roman # used to retain the original roman figure entered
lst = []
while roman != '':
if len(roman) > 1:
check = roman[0] + roman[1]
if check in roman_dict and len(roman) > 1:
lst.append(check)
roman = roman[roman.index(check[1])+1:]
else:
if check not in roman_dict and len(roman) > 1:
lst.append(check[0])
roman = roman[roman.index(check[0])+1:]
else:
if len(roman)==1:
check = roman[0]
lst.append(check[0])
roman = ''
if lst != []:
Sum = 0
for i in lst:
if i in roman_dict:
Sum += roman_dict[i]
print('The roman numeral %s entered is'%(roman_initial),Sum)
私はすべての整数> = 0で機能する答えを生成しました:
以下をromanize.pyとして保存します
def get_roman(input_number: int, overline_code: str = '\u0305') -> str:
"""
Recursive function which returns roman numeral (string), given input number (int)
>>> get_roman(0)
'N'
>>> get_roman(3999)
'MMMCMXCIX'
>>> get_roman(4000)
'MV\u0305'
>>> get_roman(4000, overline_code='^')
'MV^'
"""
if input_number < 0 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, n >= 0 are supported.')
if input_number <= 1000:
numeral, remainder = core_lookup(input_number=input_number)
else:
numeral, remainder = thousand_lookup(input_number=input_number, overline_code=overline_code)
if remainder != 0:
numeral += get_roman(input_number=remainder, overline_code=overline_code)
return numeral
def core_lookup(input_number: int) -> (str, int):
"""
Returns highest roman numeral (string) which can (or a multiple thereof) be looked up from number map and the
remainder (int).
>>> core_lookup(3)
('III', 0)
>>> core_lookup(999)
('CM', 99)
>>> core_lookup(1000)
('M', 0)
"""
if input_number < 0 or input_number > 1000 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, 0 <= n <= 1000 are supported.')
basic_lookup = NUMBER_MAP.get(input_number)
if basic_lookup:
numeral = basic_lookup
remainder = 0
else:
multiple = get_multiple(input_number=input_number, multiples=NUMBER_MAP.keys())
count = input_number // multiple
remainder = input_number % multiple
numeral = NUMBER_MAP[multiple] * count
return numeral, remainder
def thousand_lookup(input_number: int, overline_code: str = '\u0305') -> (str, int):
"""
Returns highest roman numeral possible, that is a multiple of or a thousand that of which can be looked up from
number map and the remainder (int).
>>> thousand_lookup(3000)
('MMM', 0)
>>> thousand_lookup(300001, overline_code='^')
('C^C^C^', 1)
>>> thousand_lookup(30000002, overline_code='^')
('X^^X^^X^^', 2)
"""
if input_number <= 1000 or not isinstance(input_number, int):
raise ValueError(f'Only integers, n, within range, n > 1000 are supported.')
num, k, remainder = get_thousand_count(input_number=input_number)
numeral = get_roman(input_number=num, overline_code=overline_code)
numeral = add_overlines(base_numeral=numeral, num_overlines=k, overline_code=overline_code)
# Assume:
# 4000 -> MV^, https://en.wikipedia.org/wiki/4000_(number)
# 6000 -> V^M, see https://en.wikipedia.org/wiki/6000_(number)
# 9000 -> MX^, see https://en.wikipedia.org/wiki/9000_(number)
numeral = numeral.replace(NUMBER_MAP[1] + overline_code, NUMBER_MAP[1000])
return numeral, remainder
def get_thousand_count(input_number: int) -> (int, int, int):
"""
Returns three integers defining the number, number of thousands and remainder
>>> get_thousand_count(999)
(999, 0, 0)
>>> get_thousand_count(1001)
(1, 1, 1)
>>> get_thousand_count(2000002)
(2, 2, 2)
"""
num = input_number
k = 0
while num >= 1000:
k += 1
num //= 1000
remainder = input_number - (num * 1000 ** k)
return num, k, remainder
def get_multiple(input_number: int, multiples: iter) -> int:
"""
Given an input number(int) and a list of numbers, finds the number in list closest (rounded down) to input number
>>> get_multiple(45, [1, 2, 3])
3
>>> get_multiple(45, [1, 2, 3, 44, 45, 46])
45
>>> get_multiple(45, [1, 4, 5, 9, 10, 40, 50, 90])
40
"""
options = sorted(list(multiples) + [input_number])
return options[options.index(input_number) - int(input_number not in multiples)]
def add_overlines(base_numeral: str, num_overlines: int = 1, overline_code: str = '\u0305') -> str:
"""
Adds overlines to input base numeral (string) and returns the result.
>>> add_overlines(base_numeral='II', num_overlines=1, overline_code='^')
'I^I^'
>>> add_overlines(base_numeral='I^I^', num_overlines=1, overline_code='^')
'I^^I^^'
>>> add_overlines(base_numeral='II', num_overlines=2, overline_code='^')
'I^^I^^'
"""
return ''.join([char + overline_code*num_overlines if char.isalnum() else char for char in base_numeral])
def gen_number_map() -> dict:
"""
Returns base number mapping including combinations like 4 -> IV and 9 -> IX, etc.
"""
mapping = {
1000: 'M',
500: 'D',
100: 'C',
50: 'L',
10: 'X',
5: 'V',
1: 'I',
0: 'N'
}
for exponent in range(3):
for num in (4, 9,):
power = 10 ** exponent
mapping[num * power] = mapping[1 * power] + mapping[(num + 1) * power]
return mapping
NUMBER_MAP = gen_number_map()
if __name__ == '__main__':
import doctest
doctest.testmod(verbose=True, raise_on_error=True)
# Optional extra tests
# doctest.testfile('test_romanize.txt', verbose=True)
役に立つ場合に備えて、いくつかの追加のテストを以下に示します。以下をtest_romanize.txtとしてromanize.pyと同じディレクトリに保存します。
The ``romanize`` module
=======================
The ``get_roman`` function
--------------------------
Import statement:
>>> from romanize import get_roman
Tests:
>>> get_roman(0)
'N'
>>> get_roman(6)
'VI'
>>> get_roman(11)
'XI'
>>> get_roman(345)
'CCCXLV'
>>> get_roman(989)
'CMLXXXIX'
>>> get_roman(989000000, overline_code='^')
'C^^M^^L^^X^^X^^X^^M^X^^'
>>> get_roman(1000)
'M'
>>> get_roman(1001)
'MI'
>>> get_roman(2000)
'MM'
>>> get_roman(2001)
'MMI'
>>> get_roman(900)
'CM'
>>> get_roman(4000, overline_code='^')
'MV^'
>>> get_roman(6000, overline_code='^')
'V^M'
>>> get_roman(9000, overline_code='^')
'MX^'
>>> get_roman(6001, overline_code='^')
'V^MI'
>>> get_roman(9013, overline_code='^')
'MX^XIII'
>>> get_roman(70000000000, overline_code='^')
'L^^^X^^^X^^^'
>>> get_roman(9000013, overline_code='^')
'M^X^^XIII'
>>> get_roman(989888003, overline_code='^')
'C^^M^^L^^X^^X^^X^^M^X^^D^C^C^C^L^X^X^X^V^MMMIII'
The ``get_thousand_count`` function
--------------------------
Import statement:
>>> from romanize import get_thousand_count
Tests:
>>> get_thousand_count(13)
(13, 0, 0)
>>> get_thousand_count(6013)
(6, 1, 13)
>>> get_thousand_count(60013)
(60, 1, 13)
>>> get_thousand_count(600013)
(600, 1, 13)
>>> get_thousand_count(6000013)
(6, 2, 13)
>>> get_thousand_count(999000000000000000000000000999)
(999, 9, 999)
>>> get_thousand_count(2005)
(2, 1, 5)
>>> get_thousand_count(2147483647)
(2, 3, 147483647)
The ``core_lookup`` function
--------------------------
Import statement:
>>> from romanize import core_lookup
Tests:
>>> core_lookup(2)
('II', 0)
>>> core_lookup(6)
('V', 1)
>>> core_lookup(7)
('V', 2)
>>> core_lookup(19)
('X', 9)
>>> core_lookup(900)
('CM', 0)
>>> core_lookup(999)
('CM', 99)
>>> core_lookup(1000)
('M', 0)
>>> core_lookup(1000.2)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
>>> core_lookup(10001)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
>>> core_lookup(-1)
Traceback (most recent call last):
ValueError: Only integers, n, within range, 0 <= n <= 1000 are supported.
The ``gen_number_map`` function
--------------------------
Import statement:
>>> from romanize import gen_number_map
Tests:
>>> gen_number_map()
{1000: 'M', 500: 'D', 100: 'C', 50: 'L', 10: 'X', 5: 'V', 1: 'I', 0: 'N', 4: 'IV', 9: 'IX', 40: 'XL', 90: 'XC', 400: 'CD', 900: 'CM'}
The ``get_multiple`` function
--------------------------
Import statement:
>>> from romanize import get_multiple
>>> multiples = [0, 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000]
Tests:
>>> get_multiple(0, multiples)
0
>>> get_multiple(1, multiples)
1
>>> get_multiple(2, multiples)
1
>>> get_multiple(3, multiples)
1
>>> get_multiple(4, multiples)
4
>>> get_multiple(5, multiples)
5
>>> get_multiple(6, multiples)
5
>>> get_multiple(9, multiples)
9
>>> get_multiple(13, multiples)
10
>>> get_multiple(401, multiples)
400
>>> get_multiple(399, multiples)
100
>>> get_multiple(100, multiples)
100
>>> get_multiple(99, multiples)
90
The ``add_overlines`` function
--------------------------
Import statement:
>>> from romanize import add_overlines
Tests:
>>> add_overlines('AB')
'A\u0305B\u0305'
>>> add_overlines('A\u0305B\u0305')
'A\u0305\u0305B\u0305\u0305'
>>> add_overlines('AB', num_overlines=3, overline_code='^')
'A^^^B^^^'
>>> add_overlines('A^B^', num_overlines=1, overline_code='^')
'A^^B^^'
>>> add_overlines('AB', num_overlines=3, overline_code='\u0305')
'A\u0305\u0305\u0305B\u0305\u0305\u0305'
>>> add_overlines('A\u0305B\u0305', num_overlines=1, overline_code='\u0305')
'A\u0305\u0305B\u0305\u0305'
>>> add_overlines('A^B', num_overlines=3, overline_code='^')
'A^^^^B^^^'
>>> add_overlines('A^B', num_overlines=0, overline_code='^')
'A^B'
別の方法。私はローマ記号で再帰ループを作成したので、再帰の最大深度はローマタプルの長さと等しくなります。
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 get_romans(number):
return do_recursive(number, 0, '')
def do_recursive(number, index, roman):
while number >= ROMAN[index][0]:
number -= ROMAN[index][0]
roman += ROMAN[index][1]
if number == 0:
return roman
return check_recursive(number, index + 1, roman)
if __name__ == '__main__':
print(get_romans(7))
print(get_romans(78))
aから1000,900 ...を1に減算し始め、正が見つかったときに停止します。対応するローマ数字をansに追加し、AをAiにします。ここで、iは(1,4,5,9,10 .....)です。 Aは0にはなりません。
def intToRoman(self, A):
l=[[1,'I'],[4,'IV'],[5,'V'],[9,'IX'],[10,'X'],[40,'XL'],[50,'L'],
[90,'XC'],[100,'C'],[400,'CD'],[500,'D'],[900,'CM'],[1000,'M']]
ans=""
while(A>0):
for i,j in l[::-1]:
if A-i>=0:
ans+=j
A=A-i
break
return ans
これは、数値をローマ数字に変換するための私の再帰的な関数アプローチです
def solution(n):
# TODO convert int to roman string
string=''
symbol=['M','D','C','L','X','V','I']
value = [1000,500,100,50,10,5,1]
num = 10**(len(str(n))-1)
quo = n//num
rem=n%num
if quo in [0,1,2,3]:
string=string+symbol[value.index(num)]*quo
Elif quo in [4,5,6,7,8]:
tem_str=symbol[value.index(num)]+symbol[value.index(num)-1]
+symbol[value.index(num)]*3
string=string+tem_str[(min(quo,5)-4):(max(quo,5)-3)]
else:
string=string+symbol[value.index(num)]+symbol[value.index(num)-2]
if rem==0:
return string
else:
string=string+solution(rem)
return string
print(solution(499))
print(solution(999))
print(solution(2456))
print(solution(2791))
CDXCIX
CMXCIX
MMCDLVI
MMDCCXCI
これを解決するための私のアプローチを以下に示します。指定された数値は最初に文字列に変換されるため、すべての数字を簡単に繰り返して、対応する数字のローマ字部分を取得できます。各桁のローマ字部分を取得するために、ローマ字を小数位ごとに1と5にグループ化したので、小数位に基づいてグループ化されたローマ字のリストは[['I'、 'V']、['X '、' L ']、[' C '、' D ']、[' M ']]。ここで、文字は、1、数十、数百、数千の順序に従います。
したがって、ループする数字と、小数点以下の各桁のローマ字があるので、上記の文字リストを使用して数字0〜9を準備するだけです。変数 "order"は、現在の桁の小数点位置に基づいて正しい文字セットを選択します。これは、小数点以下の最高位から最低位に移動するときに自動的に処理されます。以下は完全なコードです。
def getRomanNumeral(num):
# ---------- inner function -------------------
def makeRomanDigit(digit, order):
chars = [['I', 'V'], ['X', 'L'], ['C', 'D'], ['M']]
if(digit == 1):
return chars[order][0]
if(digit == 2):
return chars[order][0] + chars[order][0]
if(digit == 3):
return chars[order][0] + chars[order][0] + chars[order][0]
if(digit == 4):
return chars[order][0] + chars[order][1]
if(digit == 5):
return chars[order][1]
if(digit == 6):
return chars[order][1] + chars[order][0]
if(digit == 7):
return chars[order][1] + chars[order][0] + chars[order][0]
if(digit == 8):
return chars[order][1] + chars[order][0] + chars[order][0] + chars[order][0]
if(digit == 9):
return chars[order][0] + chars[order+1][0]
if(digit == 0):
return ''
#--------------- main -----------------
str_num = str(num)
order = len(str_num) - 1
result = ''
for digit in str_num:
result += makeRomanDigit(int(digit), order)
order-=1
return result
いくつかのテスト:
getRomanNumeral(112)
'CXII'
getRomanNumeral(345)
'CCCXLV'
getRomanNumeral(591)
'DXCI'
getRomanNumeral(1000)
'M'
私はコードや問題へのアプローチで多くの点で改善できることを知っていますが、これがこの問題への私の最初の試みでした。