вторник, 24 декабря 2013 г.

Python конвертация из арабских в греческие

Цифр в римской системе всего 7 вот они:
Римская цифраДесятичный эквивалент
I1
V5
X10
L50
C100
D500
M1000

Мат часть взята отсюда
Из этих цифр и составляются все числа. Если цифра стоящая слева от данной цифры меньше её, то она вычитаеться из данной цифры (принцип вычитания). Если больше то складывается (принцип сложения). Например XLVII = XL (40 = 50 - 10) + V (5) + II (2) = 47. Но есть одно исключение. Если возьмем число 99 и попытаемся перевести в лоб - из 100 (С) и вычтем  единицу, то есть получается IC. Удобно, компактно, но не правильно. В классической системе римских цифр число стоящее справа (то есть из которого вычитается) должно быть не больше чем, то что слева умноженное на десять. То есть то же число 99 надо переводить буквально XC(90 = 100 - 10) + IX (9 = 10 - 1) = XCIX. То есть 49 нельзя записывать как IL, только как LXIX. Есть ещё одно правило. Нельзя делать повторения четырёх цифр подряд (исключение составляет цифра четыре, которую изображают в часах как IIII для лучшего восприятия), то есть число 40 нельзя записывать как XXXX, а только как LX. Из всех этих правил вытекает, что максимальное число, которое можно записать римскими цифрами есть MMMCMXCIX = 3999. Но не стоит отчаиваться! Этруски, которые вроде бы придумали римские цифры, были умными ребятами и сделали хитро — число подчеркнутое палочкой сверху означает количество тысяч. То есть 4000 нужно записывать как IV. Всё просто.

Отсюда сразу вытекает алгоритм действия:
  • Если число больше или равно 4000 то делим нацело на 1000 и получаем количество тысяч, засовываем их в этот же алгоритм, что бы вычислить как они выглядят в римских цифрах и их подчеркнуть сверху. И вычитаем из исходного числа эти тысячи.
  • Если меньше то
  • Берём разряд тысяч и переводим в римский эквивалент. Вычитаем их из числа.
  • Берём разряд сотен и переводим в римский эквивалент. Вычитаем их из числа.
  • Дальше также поступаем с десятками и единицами.
  • Повторяем все эти действия пока не вычтем всё.
  • Ну и полученные циферки выводим как положено - тысячи подчеркнутые сверху (если их много, если нет, то нужное количество М) и обычным стилем все остальные буквы которые у нас получились.
У меня задача проще. Буду конвертировать от 1 до 3999
Для конвертации использую такой словарь
{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'}

Решение:
def convert_a_to_r(a_num):
    dict_out = {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'}
    r_num = ''
    for cnt in [1000, 100, 10, 1]:
        num = a_num % (cnt * 10) - a_num % cnt               
        if num in dict_out.keys():
            r_num += dict_out[num]
        else:
            if (1 * cnt) < num < (4 * cnt):
                r_num += dict_out[1 * cnt] * (num / cnt)
            elif (5 * cnt) < num < (9 * cnt):
                r_num += dict_out[5 * cnt] + dict_out[1 * cnt] * (num / cnt - 5)
    return r_num
print convert_a_to_r(3818)
MMMDCCCXVIII
Ну и как обычно решение от гуру:

def convert_a_to_r(number):
    """return roman numeral using the specified integer value from range 1...3999"""
    roman = ''
    romanmappings = {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"}
    rev_keys = romanmappings.keys()
    
    """ UPD 13.01.2017 по комментариям"""
    rev_keys.sort()    
    rev_keys.reverse()

    или сразу 
    rev_keys.sort(reverse=True)


    for intVal in rev_keys:
        while number >= intVal:
            roman += romanmappings[intVal]
            number -= intVal
    return roman
print convert_a_to_r(3818)
MMMDCCCXVIII

5 комментариев:

  1. код от гуру не заработает:
    1. у rev_keys нет метода reverse()
    2. а если бы и был, ключи в словаре хранятся в случайном порядке, а не в том порядке, в котором были инициализированы

    ОтветитьУдалить
    Ответы
    1. Почему?
      dict.keys()
      Returns list of dictionary dict's keys

      rev_keys - это список и реверс тут работает

      Удалить
    2. Это не решает вторую проблему

      Удалить
    3. точно, сортировку добавить нужно перед реверсом
      rev_keys.sort()

      Удалить
  2. ROMAN_TABLE = ((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 checkio(number):
    if number <= 0: return ''

    rez_roma = ''
    for arab, roman in ROMAN_TABLE:
    while number >= arab:
    rez_roma += roman
    number -= arab

    return rez_roma

    ОтветитьУдалить