Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting roman numerals to arabic

I'm new to Java and I need to write a program that converts roman numerals to arabic numbers.

I cannot use some functions because I'm not allowed to change the begging neither the end of the given code. I need to do everything inside the the public static void main function.

I started to search on Google and started to code. From now, I can convert only "one-letter" numerals (as X, I, V...) to arabic numbers but I cannot do this to more than elaborated numerals (XI, CCC, IX, IV...).

Can someone help me? I'm really new to Java. It's my first program language and I'm struggling to understand it.

Here is my code:

import java.util.Scanner;

class Roman {

    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        int[] numbers = {1000, 500, 100, 50, 10, 5, 1 };
        String symbols = "MDCLXVI";

        /*******************************************
         * Complete your program
         *******************************************/

        System.out.print("Enter a roman numeral");
         final int MAX = 3999;
            Scanner keyb = new Scanner(System.in);
            String roman = keyb.next();
            roman=roman.toUpperCase();

            if(roman.matches(".*[0-9].*") || !roman.matches("[M|D|C|L|X|V|I]*")){
                System.out.println("Impossible to convert. Wrong roman numeral");
            } 

            int i = 0; //position in the string romain
            int arabic = 0; // Arabic numeral equivalent of the part of the string that
                            // has been converted so far


            int number;
            while (i < roman.length()){

                char letter = roman.charAt(i); // letter at the current position in the string


                if (letter == 'I'){
                    number = 1;
                } else if (letter == 'V'){
                    number = 5;
                } else if (letter == 'X'){
                    number = 10;
                } else if (letter == 'L'){
                    number = 50;
                } else if (letter == 'C'){
                    number = 100;
                } else if (letter == 'D'){
                    number = 500;
                } else if (letter == 'M'){
                    number = 1000;
                } else {
                    number = -1;
                }

                i++; // Move on to next position in the string

                if (i==roman.length()){
                    // There is no letter in the string following the one we have just processed.
                    // So just add the number corresponding to the single letter to arabic.

                    arabic += number;

                } else {
                    // Look at the next letter in the string.  If it has a larger Roman numeral
                    // equivalent than number, then the two letters are counted together as
                    // a Roman numeral with value (nextNumber - number).


                    number = roman.charAt(i);
                    int nextNumber = number;
                    if(nextNumber > number){
                        // Combine the two letters to get one value, and move on to next position in string.
                        arabic += (nextNumber - number);
                        i++;

                    } else {
                        // Don't combine the letters.  Just add the value of the one letter onto the number.
                        arabic += number;

                    }
                }
                System.out.println(number);
            } // end while

        /*******************************************
         * Do not change after this line.
         *******************************************/
    }
}
like image 765
Rods2292 Avatar asked Mar 14 '23 13:03

Rods2292


2 Answers

I would suggest using an enumeration for your individual roman numerals. That makes the code nicely encapsulated.

public enum Roman {
    I(1), V(5), X(10), L(50), C(100), D(500), M(1000);
    private final int value;
    private Roman(int value) {
        this.value = value;
    }
    public int toInt() {
        return value;
    }
}

Converting a single roman numeral to an integer becomes trivial. For example:

Roman.valueOf("X").toInt();

The only complex bit is coping with "IX" and "XC" type of values. The easy way to identify these is that they are the only time when the numerals are not in descending order of value. Checking for this can be added as methods to the enum itself (to continue encapsulation):

public enum Roman {
    public boolean shouldCombine(Roman next) {
        return this.value < next.value;
    }
    public int toInt(Roman next) {
        return next.value - this.value;
    }
}

Now putting it all together:

List<Roman> romans = new ArrayList<>();
input.chars().mapToObj(Character::valueOf)
    .map(Roman::valueOf).forEach(romans::add);
int value = 0;
while (!romans.isEmpty()) {
    Roman current = romans.remove(0);
    if (!romans.isEmpty() && current.shouldCombine(romans.get(0))
        value += current.toInt(romans.remove(0));
    else
        value += current.ToInt();
}

The first part of this code uses Java 8 features to convert the string to roman numerals. Let me know if you find that confusing and I'll convert it to traditional iteration.

like image 70
sprinter Avatar answered Mar 21 '23 08:03

sprinter


Here is one way to do it:

import java.util.Scanner;

class Roman {

    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);
        int[] numbers = { 1000, 500, 100, 50, 10, 5, 1 };
        String symbols = "MDCLXVI";

        /*******************************************
         * Complete your program
         *******************************************/

        System.out.print("Enter a roman numeral: ");
        final int MAX = 3999;
        Scanner keyb = new Scanner(System.in);
        String roman = keyb.next();
        keyb.close(); // don't want a resource leak

        roman = roman.toUpperCase();

        if (roman.matches(".*[0-9].*") || !roman.matches("[M|D|C|L|X|V|I]*")) {
            System.out.println("Impossible to convert. Wrong roman numeral");
        }

        int i = 0; // position in the Roman string

        int current = 0; // the current Roman numeral character to Arabic
                         // conversion

        int previous = 0; // start previous at zero, that way when
                          // current is greater than previous in the first
                          // run, nothing will be subtracted from current

        int arabic = 0; // Arabic numeral equivalent of the part of the string
                        // that has been converted so far

        while (i < roman.length()) {

            char letter = roman.charAt(i); // letter at the current position in
                                            // the string

            // switch statement is easier to read than if - else if - else
            switch (letter) {
            case ('I'):
                current = 1;
                break;
            case ('V'):
                current = 5;
                break;
            case ('X'):
                current = 10;
                break;
            case ('L'):
                current = 50;
                break;
            case ('C'):
                current = 100;
                break;
            case ('D'):
                current = 500;
                break;
            case ('M'):
                current = 1000;
                break;
            }


            if (current > previous) {
                // subtract previous * 2 because previous was added to arabic
                // once already
                arabic += current - (previous * 2);
            } else {
                // if current is less than or equal to previous then add it to
                // arabic
                arabic += current;
            }

            previous = current; // set previous equal to current to check
                                // for less-than on next iteration

            i += 1; // move on to next position in the string

        } // end while

        // print the Arabic conversion after the loop is done
        System.out.println("Arabic: " + arabic);

        /*******************************************
         * Do not change after this line.
         *******************************************/
    }
}

In your code, the conditionals after i++ are not necessary since the while(i < roman.length()) conditional stops the loop once i >= roman.length().

Yet another way to do it: switch out the while loop with a for loop and a nested for loop. The outer for loop iterates over each character in the Roman numeral string. The inner for loop iterates over each character in the symbols string. If there is a match between the Roman numeral character and the symbols character, then current is set to the corresponding index in numbers and the inner loop breaks.

for(int i = 0; i < roman.length(); i++) {
    for(int s = 0; s < symbols.length(); s++) {
        if(roman.charAt(i) == symbols.charAt(s)) {
            current = numbers[s];
            break;
        }
    }

    if (current > previous) {
        // subtract previous * 2 because previous was added to arabic
        // once already
        arabic += current - (previous * 2);
    } else {
        // if current is less than or equal to previous then add it to
        // arabic
        arabic += current;
    }

    previous = current; // set previous equal to current to check
                        // for less-than on next iteration
}
like image 33
Jonny Henly Avatar answered Mar 21 '23 08:03

Jonny Henly