Números Romanos
Objectivo
- Fazer um pequeno programa que traduza números arábicos para romanos e vice-versa.
- O utilizador terá que ter a capacidade para introduzir um número num dos 2 formatos disponíveis e o programa deverá responder com a representação do número no outro formato.
- Um número em formato arábico é constituído por 1 ou mais dígitos.
- Um número em formato romano é constituído por 1 ou mais letras em maiúsculas.
Pode-se considerar que todos os números introduzidos estão correctos no seu formato, ou seja, se for em romano é uma representação válida e o contrário o mesmo. Por especificações técnicas romanas, todos os números serão inteiros positivos, ou seja, não há zero nem negativos.
Exemplo de Input/Output
input: 119
output: CXIX
input: 2008
output: MMVIII
input: MMCCXXIX
output: 2229
Exemplos de resoluções
Haskell
{-# OPTIONS_GHC -XTypeSynonymInstances #-}
import Data.List (isPrefixOf)
import Data.Char (isDigit)
lista = [ (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" )
]
class Convertivel a where
converte :: a -> String
instance Convertivel Integer where
converte 0 = ""
converte x = valS ++ converte (x-valI)
where (valI, valS) = head $ dropWhile ((x<).fst) lista
instance Convertivel String where
converte = show . converteS
converteS [] = 0
converteS s = valI + converteS s'
where (valI, valS) = head $ dropWhile (not . (`isPrefixOf`s) . snd) lista
s' = drop (length valS) s
main = getContents >>= mapM_ (putStrLn . proc) . lines
where
proc s@(x:_) | isDigit x = converte $ toInteger $ read s
| otherwise = converte s
Solução por betovsky.
Pascal
program romanos;
uses wincrt;
const
temp='dhssd';
maxlen=100;
var
redo: boolean;
ficheiro,ficheiro2: text;
cont1,cont2,tamanho,total,aux1,aux2: longint;
digit: array[0..maxlen] of char;
tempchar: char;
roman: string;
procedure block1;
begin
for cont1:=1 to maxlen-1 do digit[cont1]:=digit[maxlen];
redo:=false;
assign(ficheiro,temp);
rewrite(ficheiro);
write(ficheiro,roman);
close(ficheiro);
roman:='';
assign(ficheiro,temp);
reset(ficheiro);
cont1:=0;
while eoln(ficheiro)=false do begin
read(ficheiro,tempchar);
digit[cont1]:=upcase(tempchar);
cont1:=cont1+1;
end;
close(ficheiro);
tamanho:=cont1;
end;
begin
assign(ficheiro2,'roman.txt');
reset(ficheiro2);
repeat
cont2:=cont2+1;
readln(ficheiro2,roman);
block1;
aux1:=tamanho;
writeln(cont2,'.');
write(' INPUT = ');
for cont1:=0 to tamanho-1 do write(digit[cont1]);
writeln(' (',aux1,')');
write(' OUTPUT = ');
{ V }
repeat
for cont1:=0 to tamanho-1 do
if (digit[cont1]='I') and (digit[cont1+1]='I') and (digit[cont1+2]='I') and (digit[cont1+3]='I') and (digit[cont1+4]='I') then
begin
cont1:=cont1+4;
roman:=roman+'V';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ IV }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='I') and (digit[cont1+1]='I') and (digit[cont1+2]='I') and (digit[cont1+3]='I') then
begin
cont1:=cont1+3;
roman:=roman+'IV';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ X }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='V') and (digit[cont1+1]='V') then
begin
cont1:=cont1+1;
roman:=roman+'X';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ IX }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='V') and (digit[cont1+1]='I') and (digit[cont1+2]='V') then
begin
cont1:=cont1+2;
roman:=roman+'IX';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ L }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='X') and (digit[cont1+1]='X') and (digit[cont1+2]='X') and (digit[cont1+3]='X') and (digit[cont1+4]='X') then
begin
cont1:=cont1+4;
roman:=roman+'L';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ XL }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='X') and (digit[cont1+1]='X') and (digit[cont1+2]='X') and (digit[cont1+3]='X') then
begin
cont1:=cont1+3;
roman:=roman+'XL';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ C }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='L') and (digit[cont1+1]='L') then
begin
cont1:=cont1+1;
roman:=roman+'C';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ XC }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='L') and (digit[cont1+1]='X') and (digit[cont1+2]='L') then
begin
cont1:=cont1+2;
roman:=roman+'XC';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ D }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='C') and (digit[cont1+1]='C') and (digit[cont1+2]='C') and (digit[cont1+3]='C') and (digit[cont1+4]='C') then
begin
cont1:=cont1+4;
roman:=roman+'D';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ CD }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='C') and (digit[cont1+1]='C') and (digit[cont1+2]='C') and (digit[cont1+3]='C') and (digit[cont1+4]='X') and
(digit[cont1+5]='C') then
begin
cont1:=cont1+5;
roman:=roman+'CD';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ M }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='D') and (digit[cont1+1]='D') then
begin
cont1:=cont1+1;
roman:=roman+'M';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
{ CM }
repeat
block1;
for cont1:=0 to tamanho-1 do
if (digit[cont1]='D') and (digit[cont1+1]='C') and (digit[cont1+2]='D') then
begin
cont1:=cont1+2;
roman:=roman+'CM';
redo:=true;
end else
roman:=roman+digit[cont1];
until redo=false;
aux2:=tamanho;
writeln(roman,' (',aux2,')');
if aux2>aux1 then runerror(0);
total:=total+abs(aux2-aux1);
write(' SAVED = ',total);
if aux1<>aux2 then write(' (+',abs(aux2-aux1),')');
writeln;
writeln;
until eof(ficheiro2)=true;
close(ficheiro2);
end.
Solução por GonçaloMendes.
Nota: Após a solução proposta, fica esta informação. O Free Pascal inclui uma função, na biblioteca strutils, que converte um número inteiro numa string que representa o número no formato romano. Fica aqui:
program romanos;
uses crt, strutils;
var s:string;
i:integer;
begin
write('Numero inteiro? '); readln(i);
s := IntToRoman(i);
write(i,' em romano: ',s);
readln;
end.
Solução por thoga31.
Python
class roman:
roman = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}
pairs = [('M', 1000), ('D', 500), ('C', 100), ('L', 50), ('X', 10), ('V', 5), ('I', 1)]
def main(self):
tmp = raw_input("Input: ")
try:
tmp = int(tmp)
except:
print self.processRoman(tmp)
else:
print self.processInt(tmp)
def processRoman(self, tmp):
return sum([self.roman[char]*({True: -1, False: 1}[self.roman[char] < self.roman[tmp[min(len(tmp)-1,x+1)]]]) for x, char in list(enumerate(tmp))[::-1]])
def processInt(self, tmp):
z = []
curr = 0
for x, y in self.pairs: #can't use self.roman because python doesn't iterate it in the order i declared it...
z += [(tmp/y)*x]
if (tmp/y) > 3 and x != 'M': #excepcao para poder ter 4000+
z = z[:-2]
z += [x+self.pairs[curr-2][0]]
tmp %= y
curr += 1
return ''.join(z)
if __name__ == '__main__':
roman().main()
Solução por djthyrax.
Ruby
require 'enumerator'
module RomanTranslator
def self.included(target)
return if target != Fixnum && target != String
func_name = 'to_roman'
call_func = 'to_roman_from_number'
func_name = 'from_roman' if target == String
call_func = 'to_number_from_roman' if target == String
target.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def #{func_name}
#{call_func}(self)
end
ruby_eval
end
private
NUM_TO_ROM = {
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'
}
ROM_TO_NUM = NUM_TO_ROM.invert
def to_roman_from_number(num)
numbers = NUM_TO_ROM.keys.sort.reverse
str = num.to_s
res = ""
zeros = str.length - 1
str.each_byte do |c|
num = c.chr.to_i * (10 ** zeros)
fill = 0
numbers.each do |v|
while ((fill + v) <= num) do
fill += v
res << NUM_TO_ROM[v]
break if v == num
end
end
zeros -= 1
end
res
end
def to_number_from_roman(roman)
array = roman.split(//)
lookahead = 1
skip = false
array.inject(0) do |result, char1|
num1 = ROM_TO_NUM[char1]
if skip
skip = false
next(result)
end
num2 = ROM_TO_NUM[array[lookahead]]
if num2.nil? || num1 >= num2 then
result += num1
else
result += num2 - num1
skip = true
end
lookahead += 1
result
end
end
end
class Fixnum
include RomanTranslator
end
class String
include RomanTranslator
end
Solução por rfelix.