0b1001 шляхів вирішення завдання перекладу римських цифр

image

Привіт друзі. Ось вам простенька задачка. Як би ви переклали арабські цифри римські використовуючи 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 IV V 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 => 
© 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 = "І"*data

base = base.replace("І"*5, "V")
base = base.replace("V"*2, "X")
base = base.replace("X"*5, "L")
base = base.replace("L"*2, "С")
base = base.replace("С"*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

place for 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:

ПС: до Речі, ще є категорія «Speedy» для рішень. І коли ти говориш, що рішення не може бути довшим ніж 4000 символів, то найшвидшим рішенням цієї задачі буде ось це. Зі зрозумілих причин я можу вам показати тільки посилання.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.