0b1001 путей решения задачи перевода чисел в римскую запись / Хабр
Привет друзья. Вот вам простенькая задачка. Как бы вы перевели арабские числа в римские используя Python? Правда с одним условием — числа не могут быть больше чем 4000.
Я думаю это должно быть просто, но позвольте я вам покажу вам серию интересных решений и не тривиальных подходов:
«13 шагов» от StefanPochmann
Очень простая идея и при этом самая популярная. Мы делаем таблицу соответствий арабских и римских чисел. Идя по таблице этих соответствий мы уменьшая арабское число и увеличиваем римское.
def checkio(n): result = '' for arabic, roman in zip((1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), 'M CM D CD C XC L XL X IX V IV I'.split()): result += n // arabic * roman n %= arabic print('({}) {} => {}'.format(roman, n, result)) return result
Я добавил функцию print для вас, чтобы решение было понятнее. И вот какой будет вывод:
>>> checkio(177) (M) 177 => (CM) 177 => (D) 177 => (CD) 177 => (C) 77 => C (XC) 77 => C (L) 27 => CL (XL) 27 => CL (X) 7 => CLXX (IX) 7 => CLXX (V) 2 => CLXXV (IV) 2 => CLXXV (I) 0 => CLXXVII 'CLXXVII'
Теперь вы видите, как на каждой итерации меняется римское и арабское числа.
«thous, hunds, tens и ones» от mdeakyne
def checkio(data): ones = ["","I","II","III","IV","V","VI","VII","VIII","IX"] tens = ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"] hunds = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"] thous = ["","M","MM","MMM","MMMM"] t = thous[data // 1000] h = hunds[data // 100 % 10] te = tens[data // 10 % 10] o = ones[data % 10] return t+h+te+o
В этом случае у нас уже есть обратное соответствие арабских к римским. При этом нам уже не нужен цикл.
«base.replace» от MaikSchoepe
def checkio(data): base = "I"*data base = base.replace("I"*5, "V") base = base.replace("V"*2, "X") base = base.replace("X"*5, "L") base = base.replace("L"*2, "C") base = base.replace("C"*5, "D") base = base.replace("D"*2, "M") base = base.replace("DCCCC", "CM") base = base.replace("CCCC", "CD") base = base.replace("LXXXX", "XC") base = base.replace("XXXX", "XL") base = base.replace("VIIII", "IX") base = base.replace("IIII", "IV") return base
Я верю, что это не самый эффективный способ решения, но один из самых веселых. Он начинается с того, что делает длинную строку из “I”, размером с переданное число. Следующей строй заменяет каждые пять символов “I” на символ “V”. Далее два “V” на “X” и так далее. В конце пути мы получим строку, которую мы искали.
«Enum» от veky
Для того, чтобы понять, как работает следующее решение вам надо знать модуль Enum. Если не знаете — есть отличный шанс погуглить его.
from enum import Enum class Roman(Enum): 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 @classmethod def encode(cls, n): for numeral in cls: rep, n = divmod(n, numeral.value) yield numeral.name * rep checkio = lambda n: ''.join(Roman.encode(n))
В целом пример работает так-же как мы видели в первом примере от StefanPochmann, но кое-каким синтаксическим сахором. Таким как Enum и yield
«A derelict battery» от veky
Все эти решения я собрал с CheckiO.
И когда пользователь публикует свое решение на этом ресурсе — он должен выбрать, в какую категорию он хочет его добавить. Есть такая категория как “Creative”, где тебе не надо сильно заморачиваться на тему скорости или как легко твое решение читается.
Единственная вещь, о который ты должен думать — это на сколько креативное и необычное твое решение.Это решение как раз из такое категории.
import formatter, functools checkio = functools.partial(formatter.AbstractFormatter.format_roman, None, 'I')
Да, вот и все. Стоит упомянуть, правда, что модуль formater задеприкейтили начиная с версии 3.4 из-за того, что мало кто его использовал. Так что мы скорее всего напишем петицию Гвидо, чтобы оставить этот модуль в Python. Своим ап-вотом за это решение — вы как-бы ставите свою подпись под этой петицией.
«Достаточно элегантно, но не очень по питоновски» от nathan.l.cook
Мы идем дальше и решения становятся тяжелее
def checkio(data): rom = ['I', 'V', 'X', 'L', 'C', 'D', 'M'] str_data = str(data) str_data = str_data[::-1] num_digits = len(str_data) ans = "" rom_pointer = 0 for place in range(num_digits): if str_data[place] in ["0", "1", "2", "3"]: ans = rom[rom_pointer] * int(str_data[place]) + ans elif str_data[place] in ["4"]: ans = rom[rom_pointer] + rom[rom_pointer + 1] + ans elif str_data[place] in ["5", "6", "7", "8"]: ans = rom[rom_pointer + 1] + rom[rom_pointer] * (int(str_data[place]) - 5) + ans elif str_data[place] in ["9"]: ans = rom[rom_pointer] + rom[rom_pointer + 2] + ans rom_pointer += 2 return ans
Знаете, когда читаешь чье то решение и первые строки, которые ты видишь это:
str_data = str(data) str_data = str_data[::-1]
Ты думаешь: “Ок, тут ща точно будет какая-то магия”
«Немного истории от» от veky (или от …)
def checkio(n:int) -> str: pool = "m2d5c2l5x2v5i" rep = lambda t: int(pool[t - 1]) def roman(n, j=0, v=1000): while True: while n >= v: yield pool[j]; n -= v if n <= 0: return k = j + 2; u = v // rep(k) if rep(k) == 2: k += 2; u //= rep(k) if n + u >= v: yield pool[k]; n += u else: j += 2; v //= rep(j) return "". join(roman(n)).upper()
Вы можете знать автора этого решения по таким книгам как The Art of Computer Programming, Concrete Mathematics, Surreal Numbers и так далее.
«Эта странная римская математика» от LukeSolo
Довольно часто ты встречаешь решения на CheckiO, и при этом ты даже не представляешь, как они работают:
from math import sqrt alpha = "IVXLCDM" one = lambda n, s: (n % 5 >> n % 5 // 4 * 2) * alpha[s] two = lambda n, s: (3 < n) * alpha[s + (3 < n) + (8 < n)] three = lambda n, s: sqrt(n) == int(sqrt(n)) and ''.join(reversed(s)) or s go = lambda n, s: three(n, two(n, s) + one(n, s)) def checkio(data, s = 0, conc = ""): d, m = divmod(data, 10) text = go(m, s) + conc return d and checkio(d, s + 2, text) or text
Но я думаю, что вы разберетесь 🙂
Спасибо
В списке используемых материалов я добавил ссылки на решения CheckiO пользователей, которые использовал в этой статье. Перейдя по ним, вы можете прочитать код-ревью других пользователей либо написать свое.
Это первый раз, когда я пытаюсь поделится такой вот коллекцией наиболее интересных решений на CheckiO. Дайте мне знать, насколько интересно вам о таком читать и на сколько вам нравится сам формат.
Для создания этой статьи использовались решения пользователей CheckiO:
- “13 шагов” от StefanPochmann
- “thous, hunds, tens и ones” от mdeakyne
- “base.replace” от MaikSchoepe
- “Enum” от veky
- “A derelict battery” от veky
- “Достаточно элегантно, но не очень по питоновски” от nathan.l.cook
- “Немного истории от” от veky (или от …)
- “Эта странная римская математика” от LukeSolo
ПС: Кстати, еще есть категория “Speedy” для решений. И когда ты говоришь, что решение задачи не может быть длиннее чем 4000 символов, то самым быстрым решением для этой задачи будет вот это. По понятным причинам я могу вам показать только ссылку.
римских цифр | Цифры вики
в: Наборы цифр, наборы натуральных цифр
Посмотреть источникРимские цифры — это набор цифр, использовавшихся римлянами для обозначения чисел.
Содержание
- 1 Определение
- 2 больших числа
- 2.1 Апостроф
- 2.2 Винкулум
- 3 В разные базы
Определение
Римские цифры определяются следующими шагами:
- Разделите число на разрядные значения. Пример: 1023 = 1000 + 20 + 3.
- Расположите части от тысяч до единиц. 1000 + 20 + 3.
- Преобразуйте детали в римские цифры в соответствии с этой таблицей:
Тысячи | Сотни | Десятки | единиц | |
---|---|---|---|---|
1 | М | С | Х | я |
2 | мм | СС | ХХ | II |
3 | МММ | ССС | ХХХ | III |
4 | компакт-диск | XL | IV | |
5 | Д | л | В | |
6 | округ Колумбия | люкс | VI | |
7 | ДКК | ЛХХ | VII | |
8 | ДККК | ЛХХХ | VIII | |
9 | см | ХС | IX |
Пример: 1000 + 20 + 3 = М + ХХ + III
Следовательно, 1023 = MXXIII.
Большие числа
Согласно 4, 40 и 400, 4000 должно быть 5000-1000. Но римской цифры для 5000 нет. Вот несколько вариантов римских цифр для больших чисел:
Апостроф
Апостроф определяется следующим образом:
|Ↄ равно 500. |ƆƆ равно 5000, а |ƆƆƆ равно 50000. Число Ɔ – это количество нулей после 50, увеличивающее предыдущее число в десять раз.
C|Ɔ равно 1000. CC|ƆƆ равно 10000, а CCC|ƆƆƆ равно 100000. Думайте о C и Ɔ как о скобках. Количество CƆ — это количество нулей после 100, что также увеличивает предыдущее в десять раз.
В этой системе нет ни D, ни M, ни вычитательных определений для больших чисел. поэтому 400 — это CCCC вместо CD. Вы просто повторяете цифры эффективным способом.
4000 — это C|ƆC|ƆC|ƆC|Ɔ (1000+1000+1000+1000).
С помощью апострофа вы можете написать любое число римскими цифрами. Пример: 123456 = 100000 + 20000 + 3000 + 400 + 50 + 6.
100000 = CCC|ƆƆƆ
20000 = CC|ƆƆCC|ƆƆ
3000 = С|ƆС|ƆС|Ɔ
400 = CCCC
50 = Л
6 = VI
Следовательно, 123456 = CCC|ƆƆƆCC|ƆƆCC|ƆƆC|ƆC|ƆC|ƆCCCCLVI
Vinculum
Вместо использования апострофов, которые могут затруднить представление числа, мы можем масштабировать число в 1000 раз, рисуя линию над числом, если результат больше или равен 4000. Следовательно, 3000 будет МММ, а 4000 будет I̅V̅. 5000 — это V̅. 10 000 — это X̅, а 100 000 — это C̅. 1 000 000 – это M̅. Этот вариант намного проще, чем вышеописанный, и поэтому многие предпочитают его.
100000 = C̅
20000 = X̅X̅
3000 = MMM
400 = CD
50 = L
6 = VI
Следовательно, 123456 = C̅X̅X̅MMMCDLVI.
В разные системы счисления
В сообществе аналитиков римские цифры были преобразованы в различные четные системы счисления, поскольку некоторые не предпочитают десятичную систему. Значения букв были скорректированы в соответствии с полномочиями новой базы. Базовые индикаторы, такие как точка Хамфри, до сих пор используются для отличия числа от десятичного.
Ниже приведена таблица значений римских цифр для десятичных и дюжинных, а также других четных оснований b{\displaystyle b}:
Символ | Значение | Десятичный | Дюжина | ||
---|---|---|---|---|---|
Я | 1 | 1 | |||
В | b2{\ displaystyle {\ frac {b} {2}}} | 5 | 9{3}}1000 | 1000; = 1 728 10 | |
Вычитающие числительные, такие как 4 = IV (в десятичной системе), также применимы к другим основаниям. Они также применяются к цифрам со значением от b4+1{\displaystyle {\frac {b}{4}}+1}до b2−1{\displaystyle {\frac {b}{2}}-1}. как 3b4+1{\displaystyle {\frac {3b}{4}}+1}до 3b2−1{\displaystyle {\frac {3b}{2}}-1}.
То есть для дюжинных римских цифр 4 пишется как IIV; так как b4 + 1 = 4 {\ displaystyle {\ frac {b} {4}} + 1 = 4}, когда b = 12 {\ displaystyle b = 12}. То же правило применимо к 5 и , записанным как IV;, IIX; и IX; соответственно.
Это будет сложно для больших баз, так как для небольших чисел требуется много символов. Однако это можно решить, определив новые буквы как другие круглые значения. (Например, десятичное число 15 в шестидесятеричных римских цифрах будет представлено как IIIIIIIIIIIIIII, что может быть упрощено как AIIIIII, определяя A как новый символ для десяти.) Пока не было известно никаких предложений по этому поводу.
Исторические | китайские цифры • египетские цифры • греческие цифры • индийско-арабские цифры • цифры майя • римские цифры |
---|---|
Предложения по трансдесятичным системам счисления | Algam • De Vlieger Argam • Система дюжины • Grecset • Heptian Vigesimal • Hillarian «Cadexal» • MBeargam • Recset |
Контент сообщества доступен по лицензии CC-BY-SA, если не указано иное.
Римские цифры
Римские цифры, система счисления в Древнем Риме, используют комбинации букв латинского алфавита для обозначения значений.
Римские цифры не имеют ни понятия числа ноль, ни жесткого понятия разрядного значения . Из-за этого длина символа цифр увеличивается и уменьшается неравномерно по мере увеличения числа.
Ниже приведены числа от 1 до 500, отображаемые римскими цифрами. Подробнее о формате чисел можно прочитать здесь
|
|
|
|
|
Ниже приведен график, показывающий, как изменяется длина этих римских цифр. Как вы можете видеть, это очень циклично: длина увеличивается по мере того, как цифра I добавляется поэтапно, а затем уменьшается, когда она проходит кратность пяти, заменяясь символом с более высоким значением.
Самая длинная римская цифра до 250 — CLXXXVIII, состоящая из девяти символов и представляющая число 188.
Первое число из обязательных десяти цифр — 288, и это CCLXXXVIII
Таблица умножения
Несмотря на отсутствие практического применения, здесь тепловая карта, показывающая длины произведения двух римских цифр. Вверху слева — I x I. Чем светлее цвета, тем короче строки, чем темнее цвета, тем длиннее строки продукта.
Каждый квадрат вправо и вниз увеличивает число на единицу. По понятным причинам диаграмма симметрична относительно ведущей диагонали, но интересно различить гармоники, из-за которых она выглядит так, как будто на графике есть кольца. На графике представлены продукты для 1-50 по каждой оси.
Использование
В наши дни римские цифры используются в основном для украшения и придают изысканность при каждом использовании.