web-dev-qa-db-ja.com

整数をローマ数字に変換する基本的なプログラム?

ユーザーが入力した整数をローマ数字に変換するコードを書こうとしています。これまでのところ:

screenshot of my code

_generate_all_of_numeral_関数のポイントは、特定の数値ごとに文字列を作成することです。たとえば、generate_all_of_numeral(2400, 'M', 2000)は、文字列_'MM'_を返します。

メインプログラムで苦労しています。まず、Mのローマ数字のカウントを見つけて、変数Mに保存します。次に、Mの数にシンボル値を掛けた値を差し引いて、次に大きい数字を処理するための次の値を取得します。

正しい方向にうなずきますか?現在、私のコードは何も出力しません。

5
skybreaker

これに対処する最良の方法の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
29
Jerome Montino

分割のない別の方法を次に示します。

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'
19
Aziz Alto

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)
11
Aristide

オンラインの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'は上線文字を出力します。例えば enter image description here

出力例:

enter image description here

3

整数からローマ数字への変換用のラムダ関数を次に示します。最大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進数を文字列に変換するためのストレートです。このようにして、リストの索引付けによって数字を分離できます。データテーブルはかなり圧縮されており、減算や除算は使用されません。

確かに、与えられた形式では、簡潔に得られたものはすべてすぐに読みやすさが失われます。パズルの時間がない人のために、リスト内包表記とラムダ関数を回避する以下のバージョンが与えられています。

ステップスルー

しかし、ここではラムダ関数のバージョンについて説明します...

後ろから前へ:

  1. 10進整数をその数字の逆の文字列に変換し、逆の数字(c)を列挙(i)します。

    ....
    for i,c in enumerate(reversed(str(a)))
    ....
    
  2. 各数字cを整数(0〜9の範囲)に戻し、それをマジックディジットストリングのリストへのインデックスとして使用します。魔法は少し後で説明されます。

    ....
    [ "", "0", "00", "000", "01",
     "1", "10", "100", "1000", "02"][int(c)]])
    ....
    
  3. 選択した魔法の数字の文字列をローマ数字の「数字」の文字列に変換します。基本的に、10進数は10進数の元の10の位に適切なローマ数字として表されます。これは、OPが使用するgenerate_all_of_numeral関数のターゲットです。

    ....
    "".join([
        "IVXLCDM"[int(d)+i*2]
        for d in <magic digit string>
    ....
    
  4. すべてを逆の順序で連結します。逆順は数字の順番ですが、順番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))

再び例外トラップを省略しましたが、少なくとも今はインライン化する場所があります。

3
d5ous

笑う男によるアプローチが機能します。順序付き辞書の使用は賢いです。しかし、彼のコードは、関数が呼び出されるたびに順序付きディクショナリを再作成し、関数内では、すべての再帰呼び出しで、関数は順序付きディクショナリ全体を上からステップ実行します。また、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'
2
Rick Graves

ほとんどの回答で、人々は"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. 1/10 = 0.1(あまり役に立たない)

    1/5 = 0.2(あまり役に立たない)

    1/1 = 1.0(うーん、何かを得ました!)

    CASE 1:したがって、商== 1の場合、整数に対応するリテラルを出力します。したがって、最良のデータ構造は辞書になります。 {10: "X", 5: "V", 1:"I"}

    「私」が印刷されます。

  2. 2/10 = 0.2

    2/5 = 0.4

    2/1 = 2

    CASE 2:したがって、商> 1の場合、それを作成した整数に対応するリテラルを出力し、それを数値から減算します。これは1になり、CASE 1に該当します。「II」が印刷されます。

  3. 3/10 = 0.3

    3/5 = 0.6

    3/1 = 3

    したがって、CASE 2: "I"、CASE 2: "II"、CASE 1: "III"

  4. ケース3:1を追加し、商== 1かどうかを確認します。

    (4 + 1)/ 10 = 0.5

    (4 + 1)/ 5 = 1

    したがって、これは、最初に除数と数値を減算し、結果に対応するリテラルを出力し、その後に除数を出力する場合です。 5-4 = 1なので、「IV」が印刷されます。

  5. (9 + 1)/ 10 == 1

    10-9 = 1。 「I」を印刷、「X」を印刷、つまり「IX」

これは10の位および100の位にも拡張されます。

  1. (90+(10 ^ 1))/ 100 = 1。

    100-90 = "X"の後に100 = "C"を印刷します。

  2. (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
1
lU5er

これを行う別の方法。 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))
0
Linus

私はこの変換をカタの練習として進めていて、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

私はそのシンプルさが好きです!モジュロ演算、条件、または複数のループは必要ありません。もちろん、namedtuplesも必要ありません。代わりにプレーンタプルまたはリストを使用できます。

0
Jorge Aranda
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'
0
tim_hong

これは私の再帰的なアプローチです

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) 
0
cardeol
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
0
Divyang Bissa

1から999まで

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)
0
user7122277

SymbolCountをグローバル変数にする必要があります。そして、()をprintメソッドで使用します。

0
Gotrank

このローマ数字のコードは、間違った文字のようなエラーをチェックしません。完全なローマ数字の文字のためだけです

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'
0
Dean Kayton

別の方法。私はローマ記号で再帰ループを作成したので、再帰の最大深度はローマタプルの長さと等しくなります。

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))
0
VolArt

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
0

これは、数値をローマ数字に変換するための私の再帰的な関数アプローチです

    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
0
Ujjwal Kant

これを解決するための私のアプローチを以下に示します。指定された数値は最初に文字列に変換されるため、すべての数字を簡単に繰り返して、対応する数字のローマ字部分を取得できます。各桁のローマ字部分を取得するために、ローマ字を小数位ごとに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'

私はコードや問題へのアプローチで多くの点で改善できることを知っていますが、これがこの問題への私の最初の試みでした。

0
Aditya Shakya