Ir para o conteúdo

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.