VB .NET - Convertendo para algarismos Romanos


Você lembra dos algarismos romanos ?

Os números romanos foram durante muito tempo a principal forma de representação numérica na Europa. Os números eram representados a partir de letras do próprio alfabeto dos romanos.

Esse sistema numérico associava uma letra a uma quantidade fixa, de acordo com a tabela a seguir: I=1 V=5 X=10 L=50 C=100 D=500 M=1000

Os números romanos devem ser escritos de acordo com algumas regras:

1 - Na numeração romana, as letras são escritas uma ao lado da outra. Quando temos uma letra maior seguida de uma menor somamos os valores, observe:

VI = 5 + 1 = 6
XII = 10 + 2 = 12
LV = 50 + 5
CCL = 100 + 100 + 50 = 250
MCCXI = 1 000 + 100 + 100 + 10 + 1 = 1211
DXX = 500 + 10 +10 = 520
MDCL = 1000 + 500 + 100 + 50 = 1650

2- Quando temos uma letra menor seguida de uma maior, subtraímos o valor da maior pelo valor da menor, veja:

IV = 5 – 1 = 4
IX = 10 – 1 = 9
XL = 50 – 10 = 40
XC = 100 – 10 = 90
CM = 1 000 – 100 = 900

Obs.:
A letra I somente aparecerá antes do V e do X.
A letra X somente aparecerá antes do L e do C
A letra C somente aparecerá antes do D e do M.

3- As letras I, X, C e M somente podem ser escritas seguidamente por três vezes.

III = 1 + 1 +1 = 3
XXX = 10 + 10 + 10 = 30
LXX = 50 + 10 + 10 = 70
MM = 1 000 + 1 000 = 2 000
CCC = 100 + 100 + 100 = 300
CCX = 100 + 100 + 10 = 210

4- Algumas letras do algarismo romano são escritas com o sinal de um traço, eles representam que os valores devem ser multiplicados por 1.000, 1.000.000 e assim respectivamente. Assim temos que :

__
V
= 5000
__
X
= 10.000
__
L
= 50.000
__
C
= 100.000
__
D
= 500.000
__
M
= 1.000.000

fonte: Brasil-Escola- http://www.brasilescola.com/matematica/algarismos-romanos.htm (acessado em novembro de 2012)

Criando um projeto VB .NET para converter inteiros para romanos

Vamos criar um projeto no Visual Basic 2010 Express Edition para converter números inteiros para algarismos romanos.

No menu File clique em New Project selecione o template Windows Forms Application, informe o nome Conversor_Arabico_Romano e clique em OK;

Neste exemplo eu vou criar um projeto Windows Forms Application com o nome Conversor_Arabico_Romano e vou definir uma função chamada NumerosArabicosParaRomanos em um módulo chamado Conversor.

A seguir inclua no formulário padrão 1 controle TextBox (txtNumeroInteiro) 1 Controle Button (btnConverter) e um controle Label(lblResultado) conforme o leiaute abaixo:

Agora no menu Project clique em Add New Item e selecione o template Module informando o nome Conversor.vb e clicando em OK;

Neste módulo vamos definir uma função chamada NumerosArabicosParaRomanos que vai receber um número inteiro entre 1 e 3999.

Vamos converter números inteiros somente entre 0 e 3999 para tornar mais simples o nosso algoritmo.

No início do módulo declare a utilização do namespace System.Text pois vamos usar a classe StringBuilder para montar o resultado.

Imports System.Text

A seguir defina o código da função NumerosArabicosParaRomanos conforme abaixo:


    Public Function NumerosArabicosParaRomanos(ByVal numero As Integer) As String

        ' valida : aceita somente valores entre 1 e 3999
        If numero < 0 OrElse numero > 3999 OrElse numero = 0 Then
            Throw New ArgumentException("O valor numérico deve estar entre 1 e 3.999.")
        End If

        Dim algarismosArabicos As Integer() = New Integer() {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
        Dim algarismosRomanos As String() = New String() {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}

        ' inicializa o string builder
        Dim resultado As New StringBuilder()

        ' percorre os valores nos arrays
        For i As Integer = 0 To 12
            ' se o numero a ser convertido é menor que o valor então anexa
            ' o numero correspondente ou o par ao resultado
            While numero >= algarismosArabicos(i)
                numero -= algarismosArabicos(i)
                resultado.Append(algarismosRomanos(i))
            End While
        Next

        ' retorna o resultado
        Return resultado.ToString()

    End Function

Executando o projeto iremos obter:

Criando um projeto VB .NET para converter romanos para inteiros

Realizar o processo inverso, ou seja , converter um número romano para inteiro é uma tarefa mais complicada pois algumas regras devem ser obedecidas.

Vamos criar um novo projeto no Visual Basic 2010 Express Edition para converter números romanos para números inteiros.

No menu File clique em New Project selecione o template Windows Forms Application, informe o nome Conversor_Romano_Arabico e clique em OK;

Neste exemplo eu vou definir uma função chamada NumerosRomanosParaInteiros em um módulo chamado Conversor para realizar a conversão.

A seguir inclua no formulário padrão 1 controle TextBox (txtNumeroRomano) 1 Controle Button (btnGerarInteiro) e um controle Label(lblResultado) conforme o leiaute abaixo:

Agora no menu Project clique em Add New Item e selecione o template Module informando o nome Conversor.vb e clicando em OK;

No início do módulo declare a enumeração abaixo:

  Private Enum DigitosRomanos
        I = 1
        V = 5
        X = 10
        L = 50
        C = 100
        D = 500
        M = 1000
    End Enum

A seguir defina o código da função NumerosRomanosParaInteiros conforme abaixo:

  Public Function NumerosRomanosParaInteiros(ByVal numeroRomano As String) As Integer

        numeroRomano = numeroRomano.ToUpper().Trim()
        If numeroRomano = "N" Then
            Return 0
        End If

        ' Os numerais que representam números que começam com um '5'(V, L e D) podem 
        ' aparecer apenas uma vez em cada numeral romano. Esta regra permite XVI, mas não VIV.
        If numeroRomano.Split("V"c).Length > 2 OrElse numeroRomano.Split("L"c).Length > 2 OrElse numeroRomano.Split("D"c).Length > 2 Then
            Throw New ArgumentException("Número romano com algarismos inválidos. O número possui um algarismo V,L ou D repetido.")
        End If

        ' Uma única letra pode ser repetida até três vezes consecutivamente sendo 
        ' que cada ocorrência será somanda. Isto significa que I é um, II e III 
        ' significa dois é três. No entanto, IIII não é permitido.
        Dim contador As Integer = 1
        Dim ultimo As Char = "Z"c
        For Each numeral As Char In numeroRomano
            ' caractere inválido ?
            If "IVXLCDM".IndexOf(numeral) = -1 Then
                Throw New ArgumentException("O algarismo com posicionamento inválido...")
            End If

            ' Duplicado?
            If numeral = ultimo Then
                contador += 1
                If contador = 4 Then
                    Throw New ArgumentException("Um algarismo romano não pode ser repetido mais de 3 vezes no mesmo número...")
                End If
            Else
                contador = 1
                ultimo = numeral
            End If
        Next

        ' Cria um ArrayList contendo os valores
        Dim ptr As Integer = 0
        Dim valores As New ArrayList()
        Dim digitoMaximo As Integer = 1000
        While ptr < numeroRomano.Length
            ' valor base do digito
            Dim numeral As Char = numeroRomano(ptr)
            Dim digito As Integer = CInt([Enum].Parse(GetType(DigitosRomanos), numeral.ToString()))

            ' Um numeral de pequena valor pode ser colocado à esquerda de um valor maior
            ' Quando isto ocorre, por exemplo IX, o menor número é subtraído do maior
            ' O dígito subtraído deve ser de pelo menos um décimo do valor do maior numeral e deve ser ou I, X ou C
            ' Valores como MCMD ou CMC não são permitidos
            If digito > digitoMaximo Then
                Throw New ArgumentException("Algarísmo com posicionamento inválido.")
            End If

            ' proximo digito
            Dim proximoDigito As Integer = 0
            If ptr < numeroRomano.Length - 1 Then
                Dim proximoNumeral As Char = numeroRomano(ptr + 1)
                proximoDigito = CInt([Enum].Parse(GetType(DigitosRomanos), proximoNumeral.ToString()))

                If proximoDigito > digito Then
                    If "IXC".IndexOf(numeral) = -1 OrElse proximoDigito > (digito * 10) OrElse numeroRomano.Split(numeral).Length > 3 Then
                        Throw New ArgumentException("Rule 3")
                    End If

                    digitoMaximo = digito - 1
                    digito = proximoDigito - digito
                    ptr += 1
                End If
            End If

            valores.Add(digito)

            ' proximo digito
            ptr += 1
        End While

        ' Outra regra é a que compara o tamanho do valor de cada numeral lido a partir da esquerda para a direita. 
        ' O valor nunca deve aumentar a partir de uma letra para a próxima. 
        ' Onde houver um numeral subtractivo, esta regra se aplica ao valor 
        ' combinado dos dois algarismos envolvidos na subtração quando comparado com a letra anterior. 
        ' Isto significa que XIX é aceitável, mas XIM e IIV não são.
        For i As Integer = 0 To valores.Count - 2
            If CInt(valores(i)) < CInt(valores(i + 1)) Then
                Throw New ArgumentException("Algarismo romano inválido. Neste caso o algarismo não pode ser maior que o anterior.")
            End If
        Next

        ' Numerais maiores devem ser colocados à esquerda dos números menores para 
        ' continuar a combinação aditiva. Assim VI é igual a seis e MDCLXI é 1.661.
        Dim total As Integer = 0
        For Each digito As Integer In valores
            total += digito
        Next
        Return total

    End Function

O código acima esta comentado e o algoritmo usado foi baseado nas regras de montagem dos números romanos.

Executando o projeto iremos obter:

Assim tratamos a conversão de inteiros para romanos e vice-versa. Eu tratei o assunto em projetos separados mas você pode juntar os dois projetos em um única solução. (basta copiar as funções no mesmo módulo e ajustar a interface).

Mesmo em uma abordagem bem simples como a que fiz neste artigo você aprende a utilizar módulos, enumeração, funções para tratamento de strings e muitos outros recursos básicos da linguagem VB .NET.

Pegue os projetos completos aqui: Conversor_Arabico_Romano.zip e Conversor_Romano_Arabico.zip

1Ts 4:13 Não queremos, porém, irmãos, que sejais ignorantes acerca dos que já dormem, para que não vos entristeçais como os outros que não têm esperança.

1Ts 4:14 Porque, se cremos que Jesus morreu e ressurgiu, assim também aos que dormem, Deus, mediante Jesus, os tornará a trazer juntamente com ele.

1Ts 4:15 Dizemo-vos, pois, isto pela palavra do Senhor: que nós, os que ficarmos vivos para a vinda do Senhor, de modo algum precederemos os que já dormem.

1Ts 4:16 Porque o Senhor mesmo descerá do céu com grande brado, à voz do arcanjo, ao som da trombeta de Deus, e os que morreram em Cristo ressuscitarão primeiro.

1Ts 4:17 Depois nós, os que ficarmos vivos seremos arrebatados juntamente com eles, nas nuvens, ao encontro do Senhor nos ares, e assim estaremos para sempre com o Senhor.

1Ts 4:18 Portanto, consolai-vos uns aos outros com estas palavras.

Referências:


José Carlos Macoratti