Hero Image
- Mihai Surdeanu

Implementare algoritm de conversie a unui număr din format roman în format arabic

Față de formatul arab, în care pentru reprezentarea numerelor folosim cifrele arhicunoscute de la 0 la 9, în formatul roman vorbim de următoarele caractere: I, V, X, L, C, D și M. Vă aduceți cu siguranță aminte de ele din clasele primare. Să ridice mâna cine nu a auzit de reprezentarea romană a unui număr!

Tot în clasele primare am învățat și cum putem converti un număr din format roman în format arabic, folosind o arhicunoscută mapare:

Simbol Valoare
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

Astăzi vom vorbi doar de conversia din format roman în format arabic. Să luăm și câteva exemplu pentru a face lucrurile și mai clare:

Date de intrare Date de ieșire
Exemplul 1 II 2
Exemplul 2 IX 9
Exemplul 3 CVII 107
Exemplul 4 CLXIV 164
Exemplul 5 MMCMXCIX 2999

Să ne amintăm așadar că numerele în format roman sunt scrise de la stânga la dreapta, de la cea mai mare valoare, la cea mai mică. Totuși, partea tricky este că VIIII nu este valid, ci IX. IX = 9 = 10 – 1. În loc de o adunare, aici vorbim de o scădere. Această soluție a fost propusă pentru a reduce numărul de caractere necesare pentru a reprezenta un număr. În cazul nostru, pentru 9 sunt 2 caractere folosite, față de 5 în reprezentarea cea mai intuitivă – VIIII. Dar până la urmă noi suntem obișnuiți cu ceva asemănător atunci când spunem ora curentă. Iată și un exemplu! Să zicem că ora curentă este 12:58 și cineva ne întreabă cât este ceasul. Cea mai la îndemână variantă este ora treispreceze fără două minute. A doua ar fi: ora doisprezece și cincizeci și opt de minute. În acest caz, prima variantă este mai convenabilă pentru că necesită mai puține cuvinte de rostit (deci este mai rapidă) și cumva te duce și la ideea că mai e foarte puțin până la ora 13. Vorbim de câteva minute înainte de ora 13, dar care de multe ori sunt nesemnificative.

Cele 6 cazuri în care scăderea este utilizată sunt:

  • I poate apărea înaintea lui V și X pentru a forma 4 sau 9.
  • X poate apărea înaintea lui L sau C pentru a forma 40 sau 90.
  • C poate apărea înaintea lui D sau M pentru a forma 400 sau 900.

Cerință

Dându-se un număr în format roman, se cere să se găsească reprezentarea lui în format arabic. În plus, se garantează faptul că numărul primit ca și input este valid. Ca și temă de casă, puteți scrie un algoritm care validează șirul de caractere primit ca și input că este reprezentat corect în format roman.

Soluție

Cea mai bună soluție este că nu vedem doar cele 7 caractere ca și simboluri posibile pentru reprezentarea romană, ci să luăm în calcul și cazurile particulare, unde avem scăderi, în loc de adunări. Și anume: IV = 4, IX = 9, XL = 40, XC = 90, CD = 400, CM = 900. Cu alte cuvinte, vom aveam 13 simboluri în loc de 7. În acest fel, dintr-o singură traversare, vom putea interpreta și converti numărul dorit.

Să luăm și un exemplu pentru a clarifica lucrurile: MMCMXCIX. Iată din ce simboluri e compus acest număr: M M CM XC IX = 1000 + 1000 + 900 + 90 + 9 = 2999.

Implementarea în Java este:

import org.assertj.core.api.Assertions;
import org.junit.Test;

import java.util.Map;

import static java.util.Map.entry;

public final class NumberFormat {

    private static Map<String, Integer> CONVERT_VALUES = Map.ofEntries(
            entry("I", 1),
            entry("V", 5),
            entry("X", 10),
            entry("L", 50),
            entry("C", 100),
            entry("D", 500),
            entry("M", 1000),
            entry("IV", 4),
            entry("IX", 9),
            entry("XL", 40),
            entry("XC", 90),
            entry("CD", 400),
            entry("CM", 900)
    );

    public int romanToArabic(String number) {
        int numberLength = number.length();
        int sum = 0;

        int i = 0;
        while (i++ < numberLength) {
            if (i < numberLength) {
                String doubleSymbol = number.substring(i - 1, i + 1);
                if (CONVERT_VALUES.containsKey(doubleSymbol)) {
                    sum += CONVERT_VALUES.get(doubleSymbol);
                    i++;
                    continue;
                }
            }
            sum += CONVERT_VALUES.get(String.valueOf(number.charAt(i - 1)));
        }

        return sum;
    }

}

Iată și două teste pentru soluția propusă:

import org.assertj.core.api.Assertions;
import org.junit.Test;

public class NumberFormatTest {

    @Test
    public void simple_test() {
        Assertions.assertThat(new NumberFormat().romanToArabic("MMCMXCIX")).isEqualTo(2999);
    }

    @Test
    public void advanced_test() {
        Assertions.assertThat(new NumberFormat().romanToArabic("MMMDCCLXXXIV")).isEqualTo(3784);
    }

}

Happy coding! 🙂

Other Related Posts:

Microbenchmarking with Java

Microbenchmarking with Java

Hello guys,

Today, we are going to continue the series of articles about Java ecosystem. More specific, today, we are going to talk about JMH (Java Microbenchmark Harness).

2nd Apr 2022 - Mihai Surdeanu