Jump to content
Lukas S.

Criar numeros Romanos

Recommended Posts

Lukas S.

Objectivo

Após o utilizador colocar os números na numeração actual o programa deverá converter para a numeração romana e apresentar o output

Restrições

- Não pode usar sites de conversão

-Deverá detectar se é numeração romana ou numeração árabe (caso for romana converter para árabe(não é um requisito mas quem tiver empolgado poderá fazer)

-Nada de procurar no google ... o desafio serve para testar os seus conhecimentos não o do Google

Input/Output:

cuXl0.png

Algo parecido a isto ... ;) Espero ver ai gente a conseguir


E o Impossível foi criado por pessoas fracas pra acabar com o sonho das pessoas fortes. Não deixes que acabem com o teu. Sonha , luta , ambiciona e realiza. Se amas , se gostas tu vais conseguir. Cala todas as pessoas que um dia duvidaram de ti e prova que foste mais forte de qualquer outro.

Share this post


Link to post
Share on other sites
Lukas S.

Comentários desses são desnecessários ... O objectivo deste tópico é colocar soluções ... se achas que é fácil de mais prova-o...

Ps:Nimguem disse que era fácil ou dificil... é um desafio e está no lugar correcto ;)


E o Impossível foi criado por pessoas fracas pra acabar com o sonho das pessoas fortes. Não deixes que acabem com o teu. Sonha , luta , ambiciona e realiza. Se amas , se gostas tu vais conseguir. Cala todas as pessoas que um dia duvidaram de ti e prova que foste mais forte de qualquer outro.

Share this post


Link to post
Share on other sites
pmg

A minha versão, em C99 (por causa do strtoul()).

Os números a ser convertidos são introduzidos como parametros ao programa, por exemplo

executavel 1999 2012

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

void printroman(unsigned n) {
 static const unsigned val[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1, 0};
 static const char symbol[] = "XCMCDIXLIV";
 static const int base[] = {2, 1, 4, 3, 1, 0, 7, 6, 0, 5, 9, 8, 5};
 int index = 0;

 while (val[index]) {
   while (n >= val[index]) {
     printf("%.*s", index % 2 + 1, symbol + base[index]);
     n -= val[index];
   }
   index++;
 }
}

int main(int argc, char **argv) {
 if (argc < 2) {
   fprintf(stderr, "syntax: %s n n n ...\n", argv[0][0] ? argv[0] : "executable");
   fprintf(stderr, "    where each n is a positive integer\n");
   fprintf(stderr, "    from 1 to 3999 (inclusive).\n");
 } else {
   int argi = 1;
   while (argv[argi]) {
     unsigned n;
     char *err;

     errno = 0;
     n = strtoul(argv[argi], &err, 10);
     if (errno == ERANGE) {
       fprintf(stderr, "%s is too large.\n", argv[argi]);
     } else {
       if (errno) {
         fprintf(stderr, "unspecified error for %s.\n", argv[argi]);
       } else {
         if (*err) {
           fprintf(stderr, "%s is an invalid number.\n", argv[argi]);
         } else {
           if (n == 0) {
             fprintf(stderr, "Zero is not representable.\n");
           } else {
             if (n >= 4000) {
               fprintf(stderr, "%s is too large for text-only.\n", argv[argi]);
             } else {
               printf("%u ==> ", n);
               printroman(n);
               puts("");
             }
           }
         }
       }
     }
     argi++;
   }
 }
 return 0;
}

Edited by pmg

What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Share this post


Link to post
Share on other sites
thoga31

desafio demasiado simples ... não me apela

Eu sou directo e frontal naquilo que penso, pelo que não vais gostar, mas não me interessa. Pelo que, de facto, @HappyHippyHippo, estes comentários são desnecessários. Se te queres armar em carapau de corrida, procura outras casas para isso. No P@P cultiva-se o respeito e, acima de tudo, a humildade, não o exibicionismo barato.

@Lukas S., esse desafio já é velho. Mas é sempre bom relembrar a velha tradução de árabe para romano e vice-versa. É quase como voltar à primária :D

Em Pascal a unit strutils já fornece os métodos para essa conversão, salvo erro IntToRoman e RomanToInt.

Quando chegar a casa poderei pegar neste desafio se arranjar um tempinho após os meus outros afazeres e criar um algoritmo sem recorrer a esses procedimentos "pré-feitos", mas não vou desenvolver o sistema de detecção automático, faço com menus e crio as funções booleanas IsNumeric e MaybeIsRoman :P

  • Vote 1

Knowledge is free!

Share this post


Link to post
Share on other sites
HappyHippyHippo

Eu sou directo e frontal naquilo que penso, pelo que não vais gostar, mas não me interessa. Pelo que, de facto, @HappyHippyHippo, estes comentários são desnecessários. Se te queres armar em carapau de corrida, procura outras casas para isso. No P@P cultiva-se o respeito e, acima de tudo, a humildade, não o exibicionismo barato.

não tenho problema nenhum com o que disseste


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
KTachyon

Eu optei por extrair directamente os valores a partir da base romana. Como nunca se fazem subtracções de 500, 50 ou 5, é necessário apenas fazer a verificação por possíveis subtracções de 100, 10 ou 1.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
   int dec[] = {1000, 500, 100, 50, 10, 5, 1};
   char *rom = "MDCLXVI";

   int num, i, j;
   int read = scanf("%d", &num);

   if (num <= 0 || !read) { printf("erro"); return 1; }

   for (i = 0; i < strlen(rom); i++) {
       int r1, r2 = 0;
       r1 = num / dec[i];
       int odd = i % 2;

       if (odd && !r1)
           r2 = (num + dec[i+1]) / dec[i];
       else if (!r1)
           r2 = (num + dec[i+2]) / dec[i];

       if (r2 > 0) {
           char f = (odd ? rom[i+1] : rom[i+2]);
           for (j = 0; j < r2; j++) printf("%c%c", f, rom[i]);
           num = (odd ? num % (dec[i] - dec[i+1]) : num % (dec[i] - dec[i+2]));
       }
       else { 
           for (j = 0; j < r1; j++) printf("%c", rom[i]);
           num = num % dec[i];
       }

       if (r1) i--;
   }

   return 0;
}

Edited by KTachyon

“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Share this post


Link to post
Share on other sites
pmg

Eu optei por extrair directamente os valores a partir da base romana. Como nunca se fazem subtracções de 500, 50 ou 5, é necessário apenas fazer a verificação por possíveis subtracções de 100, 10 ou 1.

#include <stdio.h>

int main(int argc, char *argv[]) {
   int dec[] = {1000, 500, 100, 50, 10, 5, 1};
   char *rom = "MDCLXVI";

   int num, i, j;
   int read = scanf("%d", &num);

   if (num <= 0 || !read) { printf("erro"); return 1; }

   for (i = 0; i < sizeof(rom)-1; i++) {
       /* ... */
   }
   return 0;
}

O teu sizeof(rom) está mal.

O que vai devolver é o tamanho dum ponteiro (que por acaso no teu computador deve ser 8) e não o tamanho dum array com 8 caracteres.

Ou defines rom como um array

char rom[] = "MDCLXVI";

ou usas strlen()

    for (i = 0; i < strlen(rom); i++) {


What have you tried?

Não respondo a dúvidas por PM

A minha bola de cristal está para compor; deve ficar pronta para a semana.

Torna os teus tópicos mais atractivos e legíveis usando a tag CODE para colorir o código!

Share this post


Link to post
Share on other sites
thoga31

Não estou em casa, mas decidi fazer aqui num instante uma solução não elegante em Python. Logo à noite melhoro com um ciclo for para evitar esses ifs os quais reconheço serem, em parte, desnecessários.

Foi apenas uma "rapidinha" :P

def Num2Rom(n):
   romano = ""
   dif = 0

   if len(n) >= 4:
           for i in range(int(n[:-3])):
                   romano += "M"
           n = n[len(n[:-3]):] 

   if len(n) == 3:
           if int(n[0]) == 4:
                   romano += "CD"
           else:
                   if int(n[0]) >= 5:
                           romano += "D"
                           dif = 5
                   for i in range(int(n[0]) - dif):
                           romano += "C"
           n = n[1:]
           dif = 0

   if len(n) == 2:
           if int(n[0]) == 4:
                   romano += "XL"
           else:
                   if int(n[0]) >= 5:
                           romano += "L"
                           dif = 5
                   for i in range(int(n[0]) - dif):
                           romano += "X"
           n = n[1:]
           dif = 0

   if len(n) == 1:
           if int(n[0]) == 4:
                   romano += "IV"
           else:
                   if int(n[0]) >= 5:
                           romano += "V"
                           dif = 5
                   for i in range(int(n[0]) - dif):
                           romano += "I"

   return romano

#teste da função:
print(Num2Rom(input()))

http://ideone.com/0sgEw

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
KTachyon

O teu sizeof(rom) está mal.

O que vai devolver é o tamanho dum ponteiro (que por acaso no teu computador deve ser 8) e não o tamanho dum array com 8 caracteres.

Ya, estupidez. Basicamente, nem liguei à coisa, estava demasiado preocupado em acabar para arrumar e sair da "estufa".


“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Share this post


Link to post
Share on other sites
Lukas S.

A minha solução... No minimo muito difirente do que fizeram até agora ( acho). Aprendi que em alguns casos ( tal como este )o ciclo while mais o case conjugam perfeitamente pois até podemos subtrair usando o case ;) Bem fiche .. nem me lembrei que isto era possivel de ser feito ...

Module Module1
Dim romano As String
Dim input As String
Public Const arabe As String = "0123456789"
Sub Main()
 Try
	 Console.Write("Introduza um numero árabe: ")
	 input = Console.ReadLine
	 If arabe.Contains(input(0)) Then
		 romano = NumeroRomano(CInt(input))
		 Console.Write(" O resultado em Romano é: ")
		 Console.Write(romano)
		 Console.ReadKey()
	 Else
		 MsgBox("Introduza um numero Árabe")
	 End If

 Catch ex As Exception
	 MsgBox(ex.Message)
 End Try
End Sub
Public Function NumeroRomano(ByVal Numero As Integer) As String
 Try
	 Do While Numero > 0
		 Select Case Numero
			 Case Is >= 1000
				 romano &= "M"
				 Numero -= 1000
			 Case Is >= 900
				 romano &= "CM"
				 Numero -= 900
			 Case Is >= 500
				 romano &= "D"
				 Numero -= 500
			 Case Is >= 400
				 romano &= "CD"
				 Numero -= 400
			 Case Is >= 100
				 romano &= "C"
				 Numero -= 100
			 Case Is >= 90
				 romano &= "XC"
				 Numero -= 90
			 Case Is >= 50
				 romano &= "L"
				 Numero -= 50
			 Case Is >= 40
				 romano &= "XL"
				 Numero -= 40
			 Case Is >= 10
				 romano &= "X"
				 Numero -= 10
			 Case 9
				 romano &= "IX"
				 Numero -= 9
			 Case 5 To 8
				 romano &= "V"
				 Numero -= 5
			 Case 4
				 romano &= "IV"
				 Numero -= 4
			 Case 1 To 3
				 romano &= "I"
				 Numero -= 1
		 End Select
	 Loop
	 Return romano
 Catch ex As Exception
	 Console.WriteLine("Algo deu errado")
 End Try
End Function

End Module

Edited by Lukas S.

E o Impossível foi criado por pessoas fracas pra acabar com o sonho das pessoas fortes. Não deixes que acabem com o teu. Sonha , luta , ambiciona e realiza. Se amas , se gostas tu vais conseguir. Cala todas as pessoas que um dia duvidaram de ti e prova que foste mais forte de qualquer outro.

Share this post


Link to post
Share on other sites
thoga31

Uma solução final, compacta e que me dá gosto ver, pessoalmente :P

def IntToRoman(n):
conv = ((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'))
rom = ""
if isinstance(n, int) and n > 0:
	while n > 0:
		for i in conv:
			if n >= i[0]:
				rom += i[1]
				n -= i[0]
				break
return rom


Knowledge is free!

Share this post


Link to post
Share on other sites
Lukas S.

Uma solução final, compacta e que me dá gosto ver, pessoalmente :P

def IntToRoman(n):
conv = ((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'))
rom = ""
if isinstance(n, int) and n > 0:
	while n > 0:
		for i in conv:
			if n >= i[0]:
				rom += i[1]
				n -= i[0]
				break
return rom

Muito louco xD .... também usaste a subtracção correcto ?


E o Impossível foi criado por pessoas fracas pra acabar com o sonho das pessoas fortes. Não deixes que acabem com o teu. Sonha , luta , ambiciona e realiza. Se amas , se gostas tu vais conseguir. Cala todas as pessoas que um dia duvidaram de ti e prova que foste mais forte de qualquer outro.

Share this post


Link to post
Share on other sites
thoga31

Muito louco xD .... também usaste a subtracção correcto ?

Sim. Comecei por pensar na manipulação de strings, tal como apresentei primeiro, mas a solução seria mais complexa e menos eficiente. Entretanto lembrei-me da subtracção. A diferença em relação ao teu código é que eu utilizei uma List de Tuples, que é iterável, pelo que, em vez de um monte de elifs, recorri a um ciclo for, o que torna o código altamente compacto. ;)


Knowledge is free!

Share this post


Link to post
Share on other sites
Lukas S.

ótima solução ... o código compacto e completo sem falhas sendo que pode ser convertido até qualquer numero ;) bom


E o Impossível foi criado por pessoas fracas pra acabar com o sonho das pessoas fortes. Não deixes que acabem com o teu. Sonha , luta , ambiciona e realiza. Se amas , se gostas tu vais conseguir. Cala todas as pessoas que um dia duvidaram de ti e prova que foste mais forte de qualquer outro.

Share this post


Link to post
Share on other sites
thoga31

Por acaso gosto muito de compactar os códigos, ganhando ao mesmo tempo alguma eficiência. Podia dar um show de Python fazendo manipulação de strings, mas era complexo (complicado, vá xD) e ineficiente até certo ponto. E dar shows não é algo bonito. ;)

Já agora, o mesmo algoritmo mas em Pascal:

program romanos;

function IntToRoman(n : smallint) : string;
  function s2i(s : string) : integer;
  (* função auxiliar - recebe string e retorna integer *)
  begin
     val(s, s2i);
  end;

const conv : array [0..12, 0..1] of string = (('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'));
var rom : string;
   i : integer;

begin
  rom := '';
  if (n > 0) then begin
     while (n > 0) do
        for i:=0 to 12 do
           if (n >= s2i(conv[i, 0])) then begin
              rom := rom + conv[i, 1];
              n := n - s2i(conv[i, 0]);
              break;
           end;
     IntToRoman := rom;
  end else IntToRoman := 'erro';
end;


begin  (* TESTE DA FUNÇÃO *)
  writeln('2345 = ', IntToRoman(2345));
  readln;
end.

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
thoga31

Já agora, por curiosidade, uma solução numa linguagem que poucos conhecem e raros usam. TI-Basic, na sua versão Z80, onde a indentação não existe e a magia das calculadoras TI acontece. :D

Este código funcionará numa calculadora da família TI83/84, pelo que as variáveis disponíveis são as de origem. Como não é possível criar listas de strings, tem de se criar aquela estrutura de Ifs.

{1000,900,500,400,100,90,50,40,10,9,5,4,1}→L1

0→N
While N<1
Prompt N
End

""→Str0
While N>0
For(I,1,13,1)
If N>=L1(I):Then

If L1(I)=1000:"M"→Str1
If L1(I)=900:"CM"→Str1
If L1(I)=500:"D"→Str1
If L1(I)=400:"CD"→Str1
If L1(I)=100:"C"→Str1
If L1(I)=90:"XC"→Str1
If L1(I)=50:"L"→Str1
If L1(I)=40:"XL"→Str1
If L1(I)=10:"X"→Str1
If L1(I)=9:"IX"→Str1
If L1(I)=5:"V"→Str1
If L1(I)=4:"IV"→Str1
If L1(I)=1:"I"→Str1

Str0+Str1→Str0
N-L1(I)→N
14→I

End
End
End

Disp Str0


Knowledge is free!

Share this post


Link to post
Share on other sites
thoga31

btw, em Python a conversão de Romano para Árabe também não é difícil. Vou colocar o código que o faz. Mas este sistema não controla se o formato do número romano é válido, ou se é na sua totalidade romano. Estou a pressupor que o input é todo correcto.

def RomanToInt(rom):
   def espaca(s):
       r = ''
       for i in s:
           r += i + ' '
       return r

   def especial(s):
       return s.replace('C M','CM').replace('C D','CD').replace('X C','XC').replace('X L','XL').replace('I X','IX').replace('I V','IV')

   conv = ((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'))

   return sum([i[0] for j in especial(espaca(rom)).split() for i in conv if i[1] == j])

Teste:

>>> #Formato correcto:
... RomanToInt('MMXII')
2012
>>> #Formato errado - aceita na mesma:
... RomanToInt('IXIMM')
2012
>>> #Totalmente árabe - retorna nada:
... RomanToInt('2012')
0
>>> #Parte romano e parte árabe - só vai retornar aquilo que está em romano:
... RomanToInt('MM012')
2000
>>> #Frase que contém numeração romana - denotar a detecção do caso "IX" em "FIXE":
... RomanToInt('EXEMPLO FIXE')
1069


Knowledge is free!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.