Fraction calculator input error [duplicate] - java

This question already has answers here:
What is a NumberFormatException and how can I fix it?
(9 answers)
Closed 6 years ago.
I am working on a fraction calculator program for my AP Computer Science class. This code compiles and runs, however when I put in any input, besides quit, there is an error. With an input 8_9/4 + 3/7 there is an error of:
Exception in thread "main" java.lang.NumberFormatException: For input string: "4 + 3/7".
Can someone please help me figure out what's wrong?
import java.util.*;
public class FracCalc {
public static void main(String[] args) {
typeEquation();
}
public static void typeEquation() {
System.out.print("Enter an equation, or \"quit\" : ");
Scanner scan = new Scanner(System.in);
String fraction = scan.nextLine();
String secondOperator;
if (fraction.equalsIgnoreCase("quit")) {
finish();
}
else {
produceAnswer(fraction);
}
}
public static void produceAnswer(String fraction) {
int whole2;
int numerator2;
int denominator2;
int whole1;
int numerator1; //all caps identifier thing?
int denominator1;
String operator = fraction.substring((fraction.indexOf(" ")) + 1);
if (fraction.contains("_")) {
whole2 = Integer.parseInt(fraction.substring(0, fraction.indexOf("_")));
numerator2 = Integer.parseInt(fraction.substring(fraction.indexOf("_") + 1, fraction.indexOf("/")));
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
numerator2 = whole2 * denominator2 + numerator2;
}
else if (fraction.contains("/")) {
numerator2 = Integer.parseInt(fraction.substring(0,fraction.indexOf("/")));
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
}
else {
whole2 = Integer.parseInt(fraction.substring(0));
numerator2 = whole2;
denominator2 = 1;
}
if (fraction.contains("_")) {
whole1 = Integer.parseInt(fraction.substring(0, fraction.indexOf("_")));
numerator1 = Integer.parseInt(fraction.substring(fraction.indexOf("_") + 1, fraction.indexOf("/")));
denominator1 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
numerator1 = (whole1*denominator1) + numerator1;
}
else if (fraction.contains("/")) {
numerator1 = Integer.parseInt(fraction.substring(0, fraction.indexOf("/")));
denominator1 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
}
else {
whole1 = Integer.parseInt(fraction.substring(0));
numerator1 = whole1;
denominator1 = 1;
}
if (fraction.contains("_")) {
whole2 = Integer.parseInt(fraction.substring(0, fraction.indexOf("_")));
numerator2 = Integer.parseInt(fraction.substring(fraction.indexOf("_") + 1, fraction.indexOf("/")));
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
numerator2 = whole2 * denominator2 + numerator2;
}
else if (fraction.contains("/")) {
numerator2 = Integer.parseInt(fraction.substring(0, fraction.indexOf("/")));
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
}
else {
whole2 = Integer.parseInt(fraction.substring(0));
numerator2 = whole2;
denominator2 = 1;
}
if (operator.equals("+")) {
System.out.println(addingFractions(numerator1, numerator2, denominator1, denominator2));
}
else if (operator.equals("*")) {
System.out.println(multiplyingFractions(numerator1, numerator2, denominator1, denominator2));
}
else {
int x = numerator2;
int y = denominator2;
denominator2 = x;
numerator2 = y;
System.out.println(multiplyingFractions(numerator1, numerator2, denominator1, denominator2));
}
int dividend = (denominator1 * numerator2) + (numerator1 * denominator2);
int divisor = denominator1 * denominator2;
int remainder = dividend % divisor;
while (remainder != 0){
dividend = divisor;
divisor = remainder;
remainder = dividend % divisor;
}
}
public static String multiplyingFractions(int numerator1, int numerator2, int denominator1, int denominator2) {
int newNumerator = numerator1 * numerator2;
int newDenominator = denominator1 * denominator2;
int divisor = reducingFractions(newNumerator, newDenominator);
newNumerator /= divisor;
newDenominator /= divisor;
int integerComponent = 0;
while (newNumerator >= newDenominator) {
integerComponent++;
newNumerator -= newDenominator;
}
String answer = "";
if (integerComponent > 0) {
answer += integerComponent + "_";
}
if (newNumerator != 0) {
answer += reducingFractions(newNumerator, newDenominator);
}
return answer;
}
public static String addingFractions(int numerator1, int numerator2, int denominator1, int denominator2) {
int newNumerator = (numerator1 * denominator2) + (numerator2 * denominator1);
int newDenominator = denominator1 * denominator2;
int divisor = reducingFractions(newNumerator, newDenominator);
newNumerator /= divisor;
newDenominator /= divisor;
int integerComponent = 0;
while (newNumerator >= newDenominator) {
integerComponent++;
newNumerator -= newDenominator;
}
String answer = "";
if (integerComponent > 0) {
answer += integerComponent + "_";
}
if (newNumerator != 0) {
answer += newNumerator + "/" + newDenominator;
}
return answer;
}
public static int reducingFractions(int newNumerator, int newDenominator) {
int newNumerator_abs = Math.abs (newNumerator);
int newDenominator_abs = Math.abs (newDenominator);
int minimumNumber = Math.min (newNumerator_abs, newDenominator_abs);
int divisor = 1;
for (int i = 1; i <= minimumNumber; i++) {
if (newNumerator % i == 0 && newDenominator % i == 0){
divisor = 1;
}
}
return divisor;
}
public static void finish() {
System.out.println("Goodbye!");
}
}

You are getting this error, because you try to parse a String as integer, even if it isn't an integer. This is the problematic line:
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
for this input 8_9/4 + 3/7.
This fraction.indexOf("/") returns you the first occurrence of the slash. So, the following expression fraction.substring(fraction.indexOf("/") + 1) returns 4 + 3/7 which is not an integer, so it cannot be parsed using Integer.parseInt.
If you want to get only 4 instead of 4 + 3/7, you can replace your line with:
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1, fraction.indexOf(" ")));

You did not extract the "fraction" string correctly. Be careful when you do Integer.parseInt(). It doesn't take in equation.
In the code that you wrote:
else if (fraction.contains("/")) {
numerator2 = Integer.parseInt(fraction.substring(0,fraction.indexOf("/")));
denominator2 = Integer.parseInt(fraction.substring(fraction.indexOf("/") + 1));
}
The fraction.substring(0,fraction.indexOf("/") will return "8_9" as string. Which is not recognisable by Integer.parseInt.
Try to run in debug and set the breakpoint to break at Interger.parseInt for visualising the values.

Related

Using two methods in a class (Rational) to simplify a fraction

I have a class definition for a class of rational numbers. My assignment is to be able to add, multiply and divide any fraction I put in my main function. My program can do all that, but I'm having trouble simplifying the fractions. I want to try and use only two methods to simplify, for example public void reduce(); and private static gcd();
public class Rational {
private int num;
private int denom;
public Rational() {
num = 0;
denom = 1;
}
public Rational(int n, int d) {
num = n;
denom = d;
reduce();
}
public Rational plus(Rational t) {
int tnum = 0;
int tdenom = 1;
tnum = (this.num * t.denom) + (this.denom * t.num);
tdenom = (t.denom * this.denom);
Rational r = new Rational (tnum, tdenom);
return r;
}
public Rational minus(Rational t) {
int tnum = 0;
int tdenom = 1;
tnum = (this.num * t.denom) - (this.denom * t.num);
tdenom = (t.denom * this.denom);
Rational r = new Rational (tnum, tdenom);
return r;
}
public Rational multiply(Rational t) {
int tnum = 0;
int tdenom = 1;
tnum = this.num * t.num;
tdenom = t.denom * this.denom;
Rational r = new Rational (tnum, tdenom);
return r;
}
public Rational divide(Rational t) {
int tnum = 0;
int tdenom = 1;
tnum = this.num / t.num;
tdenom = this.denom / t.denom;
Rational r = new Rational (tnum, tdenom);
return r;
}
private static int gcd(int n, int d) {
return gcd(d, n%d);
}
public void reduce() {
//call gcd
gcd(num, denom);
//divide num and denom by gcd by
num = num / gcd(num,denom);
denom = denom / gcd(num,denom);
}
public String toString() {
return String.format("%d/%d", num, denom);
}
}
public class RationalMain {
public static void main(String[] args) {
Rational x = new Rational();
Rational y = new Rational(1,4);
Rational z = new Rational(1,2);
//x = y - z;
x = y.plus(z);
System.out.printf("%s = %s + %s\n", x.toString(), y.toString(), z.toString());
x = z.minus(y);
System.out.printf("%s = %s - %s\n", x.toString(), z.toString(), y.toString());
x = z.multiply(y);
System.out.printf("%s = %s * %s\n", x.toString(), z.toString(), y.toString());
x = y.divide(z);
System.out.printf("%s = %s / %s\n", x.toString(), y.toString(), z.toString());
}
}
That's not how you might achieve the Greatest Common Divisor (GCD). Before you will be able to get your code to work properly you will need to at least fix your gcd() method since currently it will recurse until an ArithmeticException (/ by zero) is generated. You might achieve the task this way:
private static int gcd(int num, int den) {
num = Math.abs(num); // if numerator is signed convert to unsigned.
int gcd = Math.abs(den); // if denominator is signed convert to unsigned.
int temp = num % gcd;
while (temp > 0) {
num = gcd;
gcd = temp;
temp = num % gcd;
}
return gcd;
}
To convert your fractions too their Lowest Terms your reduce() method might look like this if it accepted a Fraction String as an argument (you modify the method parameters if you like):
/*
A Fraction String can be supplied as: "1/2", or "2 1/2", or
"2-1/2, or "-2 32/64", or "-2-32/64". The last 2 examples are
negative fraction values.
*/
private String reduce(String fractionString) {
// Fraction can be supplied as: "1/2", or "2 1/2", or "2-1/2".
// Make sure it's a Fraction String that was supplied as argument...
inputString = inputString.replaceAll("\\s+", " ").trim();
if (!inputString.matches("\\d+\\/\\d+|\\d+\\s+\\d+\\/\\d+|\\d+\\-\\d+\\/\\d+")) {
return null;
}
str2 = new StringBuilder();
String wholeNumber, actualFraction;
if (inputString.contains(" ")) {
wholeNumber = inputString.substring(0, inputString.indexOf(" "));
actualFraction = inputString.substring(inputString.indexOf(" ") + 1);
str2.append(wholeNumber);
str2.append(" ");
}
else if (inputString.contains("-")) {
wholeNumber = inputString.substring(0, inputString.indexOf("-"));
actualFraction = inputString.substring(inputString.indexOf("-") + 1);
str2.append(wholeNumber);
str2.append("-");
}
else {
actualFraction = inputString;
}
String[] tfltParts = actualFraction.split("\\/");
int tfltNumerator = Integer.parseInt(tfltParts[0]);
int tfltDenominator = Integer.parseInt(tfltParts[1]);
// find the larger of the numerator and denominator
int tfltN = tfltNumerator;
int tfltD = tfltDenominator;
int tfltLargest;
if (tfltNumerator < 0) {
tfltN = -tfltNumerator;
}
if (tfltN > tfltD) {
tfltLargest = tfltN;
}
else {
tfltLargest = tfltD;
}
// Find the largest number that divides the numerator and
// denominator evenly
int tfltGCD = 0;
for (int tlftI = tfltLargest; tlftI >= 2; tlftI--) {
if (tfltNumerator % tlftI == 0 && tfltDenominator % tlftI == 0) {
tfltGCD = tlftI;
break;
}
}
// Divide the largest common denominator out of numerator, denominator
if (tfltGCD != 0) {
tfltNumerator /= tfltGCD;
tfltDenominator /= tfltGCD;
}
str2.append(String.valueOf(tfltNumerator)).append("/").append(String.valueOf(tfltDenominator));
return str2.toString();
}
As you can see, a whole number can also be supplied with your fraction so, if you do a call to the above reduce() method like this:
System.out.println(reduce("12-32/64"));
System.out.println(reduce("12 32/64"));
The console window will display:
12-1/2
12 1/2

Finding the largest Odd Fibonacci number in the given ranges of values

Ex: n1=100, n2=250, out=233.
Here I have to find the largest odd fibonacci number in the given set of ranges. If an odd fibonacci number doesn't exist then it should return 0. I am getting output as 50 times 0's and then 10 times 233. Where is my mistake and how can I get the desired output?
public class Fibo {
public static void main(String[] args) {
try {
int n1 = 100;
int n2 = 250;
int res = 0;
if (n1 % 2 == 0) {
n1 += 1;
for (int i = n1; i < n2; i += 2) {
if (isPerfectSquare(5 * i * i + 4) || isPerfectSquare(5 * i * i - 4))
res = i;
System.out.println(res);
}
}
} catch(Exception ignored) {
System.out.println("0");
}
}
public static boolean isPerfectSquare(int num) {
double sqrt = Math.sqrt(num);
int x = (int)sqrt;
return Math.pow(sqrt, 2) == Math.pow(x, 2);
}
}
public class Fibonacci {
public static void main(String[] args) {
System.out.println("Enter the starting range");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println("Enter the ending range");
int r = sc.nextInt();
int res = 0;
for (int i = n; i <= r; i++) {
if (isPerfectSquare(5 * i * i + 4) || isPerfectSquare(5 * i * i - 4))
res = i;
}
System.out.println("The biggest odd number in the range is"+" "+res);
}
public static boolean isPerfectSquare(int num) {
double sqrt = Math.sqrt(num);
int x = (int)sqrt;
return Math.pow(sqrt, 2) == Math.pow(x, 2);
}
}
public static int getLargestOddFibonacciBetween(int lo, int hi) {
assert lo <= hi;
int f0 = 0;
int f1 = 1;
int res = -1;
while (f1 <= hi) {
int val = f0 + f1;
f0 = f1;
f1 = val;
if (val >= lo && val <= hi && isOdd(val))
res = val;
}
return res;
}
private static boolean isOdd(int val) {
return (val & 1) == 1;
}

Generating two random prime numbers in JAVA

I'm trying to generate two random prime numbers in JAVA, however, I want the loop to keep repeating itself until both of those two variables are prime numbers, and then they output themselves.
The p and q variables are randomized by the Math.random() function and are in the range of 2 to 128 (excluding the 128).
Here is my code:
int pRandom = (int) (Math.random() * (127 - 2) + 2);
int qRandom = (int) (Math.random() * (127 - 2) + 2);
int p = pRandom;
int q = qRandom;
for (int i = 1; i < p; i++) {
boolean isPPrime = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
isPPrime = false;
break;
}
}
if (isPPrime){
JOptionPane.showMessageDialog(null, "YAY!");
break;
}
System.out.println("P value: " + p + "\n" + "Q value: " + q);
}
Here is what you want:
public class RandomPrimeGenerator {
public static void main(String[] args) {
while (true) {
int pRandom = (int) (Math.random() * (127 - 2) + 2);
if(isPrime(pRandom)){
System.out.println("Got Random Prime P :"+pRandom);
break;
}
}
while(true){
int qRandom = (int) (Math.random() * (127 - 2) + 2);
if(isPrime(qRandom)){
System.out.println("Got Random Prime Q :"+qRandom);
break;
}
}
}
private static boolean isPrime(int n) {
int i;
for(i=2;i<=Math.sqrt(n);i++){
if(n % i == 0){
return false;
}
}
return true;
}
}

String index out of range (Repeating Sequence of digits)

I seem to be having a problem with my code which is to look for the repeating sequence of digits. I have converted(?) double to string because I get the error unreachable statement. (which I guess helps to looking for the reason why I get the error I have now?).
Whenever I run it, it goes fine until I finish entering N and D.
It'll say "Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 3"
Here is my code below:
import java.util.*;
public class RepeatingSequence{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
System.out.print("Enter N,D: ");
int numerator = in.nextInt();
int denominator = in.nextInt();
double quotient = numerator / denominator;
String number = "" + quotient;
char n = number.charAt(0);
int j = 1;
int z = 0;
String output = "";
char[] index = number.toCharArray();
for ( int i = 2; number.charAt(j) != number.charAt(i); i++ ){
index[z] = number.charAt(z);
index[j] = number.charAt(j);
index[i] = number.charAt(i);
output = output + index[i];
if ( index[i] != index[z] ){
System.out.print(index[z] + ".(" + index[j] + output + ")");
}
}
}
}
just add i < number.length() to the condition
( int i = 2; i < number.length() && number.charAt(j) != number.charAt(i); i++ )
For your exception, I think you should write safer code - something on the lines of:
int len = number.length();
for ( int i = 2; (i < len) && (j < len) &&
number.charAt(j) != number.charAt(i); i++ ){
...
}
I am not attempting to solve the problem that you are trying to solve but just the problem you are facing. Sorry for that.
I changed your code a little bit try it out and see what you think:
public static void main(String[] args){
Scanner in = new Scanner(System.in);
System.out.print("Enter N,D: ");
double numerator = in.nextDouble();
double denominator = in.nextDouble();
double quotient = numerator / denominator;
String number = "" + quotient;
char n = number.charAt(0);
int j = 1;
int z = 0;
String output = "";
char[] index = number.toCharArray();
int max = -1;
int currentNumber = -1;
int temp = -1;
int tempMax = -1;
System.out.println("" + quotient);
boolean check = true;
for(int i = (number.indexOf(".") + 1); i < index.length; i++)
{
if(max == -1)
{
currentNumber = i;
temp = i;
max = 1;
tempMax = 1;
}
else
{
if(index[i] == index[i-1])
{
tempMax++;
}
else
{
if(tempMax > max)
{
check = false;
max = tempMax;
currentNumber = temp;
}
tempMax = 1;
temp = i;
}
}
}
if(check)
{
max = tempMax;
}
System.out.println(index[currentNumber] + " repeats " + max + " times.");
}
Example of input/output:
Enter N,D: 1
3
0.3333333333333333
3 repeats 16 times.

Converting Roman Numerals To Decimal

I have managed to get my code to convert most Roman numerals to its appropriate decimal value. But it doesn't work for some exceptional cases. Example : XCIX = 99 but my code prints 109.
Here is my code.
public static int romanConvert(String roman)
{
int decimal = 0;
String romanNumeral = roman.toUpperCase();
for(int x = 0;x<romanNumeral.length();x++)
{
char convertToDecimal = roman.charAt(x);
switch (convertToDecimal)
{
case 'M':
decimal += 1000;
break;
case 'D':
decimal += 500;
break;
case 'C':
decimal += 100;
break;
case 'L':
decimal += 50;
break;
case 'X':
decimal += 10;
break;
case 'V':
decimal += 5;
break;
case 'I':
decimal += 1;
break;
}
}
if (romanNumeral.contains("IV"))
{
decimal-=2;
}
if (romanNumeral.contains("IX"))
{
decimal-=2;
}
if (romanNumeral.contains("XL"))
{
decimal-=10;
}
if (romanNumeral.contains("XC"))
{
decimal-=10;
}
if (romanNumeral.contains("CD"))
{
decimal-=100;
}
if (romanNumeral.contains("CM"))
{
decimal-=100;
}
return decimal;
}
It will be good if you traverse in reverse.
public class RomanToDecimal {
public static void romanToDecimal(java.lang.String romanNumber) {
int decimal = 0;
int lastNumber = 0;
String romanNumeral = romanNumber.toUpperCase();
/* operation to be performed on upper cases even if user
enters roman values in lower case chars */
for (int x = romanNumeral.length() - 1; x >= 0 ; x--) {
char convertToDecimal = romanNumeral.charAt(x);
switch (convertToDecimal) {
case 'M':
decimal = processDecimal(1000, lastNumber, decimal);
lastNumber = 1000;
break;
case 'D':
decimal = processDecimal(500, lastNumber, decimal);
lastNumber = 500;
break;
case 'C':
decimal = processDecimal(100, lastNumber, decimal);
lastNumber = 100;
break;
case 'L':
decimal = processDecimal(50, lastNumber, decimal);
lastNumber = 50;
break;
case 'X':
decimal = processDecimal(10, lastNumber, decimal);
lastNumber = 10;
break;
case 'V':
decimal = processDecimal(5, lastNumber, decimal);
lastNumber = 5;
break;
case 'I':
decimal = processDecimal(1, lastNumber, decimal);
lastNumber = 1;
break;
}
}
System.out.println(decimal);
}
public static int processDecimal(int decimal, int lastNumber, int lastDecimal) {
if (lastNumber > decimal) {
return lastDecimal - decimal;
} else {
return lastDecimal + decimal;
}
}
public static void main(java.lang.String args[]) {
romanToDecimal("XIV");
}
}
Try this - It is simple and compact and works quite smoothly:
public static int ToArabic(string number) {
if (number == string.Empty) return 0;
if (number.StartsWith("M")) return 1000 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("CM")) return 900 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("D")) return 500 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("CD")) return 400 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("C")) return 100 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("XC")) return 90 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("L")) return 50 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("XL")) return 40 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("X")) return 10 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("IX")) return 9 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("V")) return 5 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("IV")) return 4 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("I")) return 1 + ToArabic(number.Remove(0, 1));
throw new ArgumentOutOfRangeException("something bad happened");
}
assuming the hash looks something like this
Hashtable<Character, Integer> ht = new Hashtable<Character, Integer>();
ht.put('i',1);
ht.put('x',10);
ht.put('c',100);
ht.put('m',1000);
ht.put('v',5);
ht.put('l',50);
ht.put('d',500);
then the logic gets pretty simple going by digit right to left
public static int rtoi(String num)
{
int intNum=0;
int prev = 0;
for(int i = num.length()-1; i>=0 ; i--)
{
int temp = ht.get(num.charAt(i));
if(temp < prev)
intNum-=temp;
else
intNum+=temp;
prev = temp;
}
return intNum;
}
Following your logic of reducing 2 on IX you should reduce 20 on XC 200 on CM and so on.
Less code, more efficient. Not so clearer, sorry!
public int evaluateRomanNumerals(String roman) {
return (int) evaluateNextRomanNumeral(roman, roman.length() - 1, 0);
}
private double evaluateNextRomanNumeral(String roman, int pos, double rightNumeral) {
if (pos < 0) return 0;
char ch = roman.charAt(pos);
double value = Math.floor(Math.pow(10, "IXCM".indexOf(ch))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(ch)));
return value * Math.signum(value + 0.5 - rightNumeral) + evaluateNextRomanNumeral(roman, pos - 1, value);
}
Imperative + recursive solutions with validation step and online testing
To avoid useless calculations and to make sure the roman numerals format is correct, we need to check the input with a regular expression.
String regex = "^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$";
Explanation of the regular expression in details
Explanation of the regular expression symbols used
Differences between regex functions matches() and find()
Why is MMMCMXCIX (= 3999) the maximum value with standard roman numerals notation
Imperative solution
public static int romanToDecimal(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
final Matcher matcher = Pattern.compile("M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I").matcher(s);
final int[] decimalValues = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
final String[] romanNumerals = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
int result = 0;
while (matcher.find())
for (int i = 0; i < romanNumerals.length; i++)
if (romanNumerals[i].equals(matcher.group(0)))
result += decimalValues[i];
return result;
}
try online | try optimized version with comments/explanation online
UPDATE: here are two clever recursive proposals from this thread I solved adding validation step
Recursive solution 1 (original answer)
public class RomanToDecimalConverter {
private static double evaluateNextRomanNumeral(String roman, int pos, double rightNumeral) {
if (pos < 0) return 0;
char ch = roman.charAt(pos);
double value = Math.floor(Math.pow(10, "IXCM".indexOf(ch))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(ch)));
return value * Math.signum(value + 0.5 - rightNumeral) + evaluateNextRomanNumeral(roman, pos - 1, value);
}
public static int evaluateRomanNumerals(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
return (int) evaluateNextRomanNumeral(s, s.length() - 1, 0);
}
}
try online
Recursive solution 2 (original answer)
public class RomanToDecimalConverter {
private static int convertRec(String s) {
if (s.isEmpty()) return 0;
if (s.startsWith("M")) return 1000 + convertRec(s.substring(1));
else if (s.startsWith("CM")) return 900 + convertRec(s.substring(2));
else if (s.startsWith("D")) return 500 + convertRec(s.substring(1));
else if (s.startsWith("CD")) return 400 + convertRec(s.substring(2));
else if (s.startsWith("C")) return 100 + convertRec(s.substring(1));
else if (s.startsWith("XC")) return 90 + convertRec(s.substring(2));
else if (s.startsWith("L")) return 50 + convertRec(s.substring(1));
else if (s.startsWith("XL")) return 40 + convertRec(s.substring(2));
else if (s.startsWith("X")) return 10 + convertRec(s.substring(1));
else if (s.startsWith("IX")) return 9 + convertRec(s.substring(2));
else if (s.startsWith("V")) return 5 + convertRec(s.substring(1));
else if (s.startsWith("IV")) return 4 + convertRec(s.substring(2));
else if (s.startsWith("I")) return 1 + convertRec(s.substring(1));
throw new IllegalArgumentException("Unexpected roman numerals");
}
public static int convert(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
return convertRec(s);
}
}
try online
// Author: Francisco Edmundo
private int translateNumber(String texto) {
int n = 0;
int numeralDaDireita = 0;
for (int i = texto.length() - 1; i >= 0; i--) {
int valor = (int) translateNumber(texto.charAt(i));
n += valor * Math.signum(valor + 0.5 - numeralDaDireita);
numeralDaDireita = valor;
}
return n;
}
private double translateNumber(char caractere) {
return Math.floor(Math.pow(10, "IXCM".indexOf(caractere))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(caractere)));
}
You can check following code. This code should work on all cases. Also it checks null or empty input and faulty input (Let's say you tried with ABXI)
import java.util.HashMap;
import org.apache.commons.lang3.StringUtils;
public class RomanToDecimal {
private HashMap<Character, Integer> map;
public RomanToDecimal() {
map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
}
private int getRomanNumeralValue(char ch) {
if (map.containsKey(ch)) {
return map.get(ch);
}
else {
throw new RuntimeException("Roman numeral string contains invalid characters " + ch);
}
}
public int convertRomanToDecimal(final String pRomanNumeral) {
if (StringUtils.isBlank(pRomanNumeral)) {
throw new RuntimeException("Roman numeral string is either null or empty");
}
else {
int index = pRomanNumeral.length() - 1;
int result = getRomanNumeralValue(pRomanNumeral.charAt(index));
for (int i = index - 1; i >= 0; i--) {
if (getRomanNumeralValue(pRomanNumeral.charAt(i)) >= getRomanNumeralValue(pRomanNumeral.charAt(i + 1))) {
result = result + getRomanNumeralValue(pRomanNumeral.charAt(i));
}
else {
result = result - getRomanNumeralValue(pRomanNumeral.charAt(i));
}
}
return result;
}
}
public static void main(String... args){
System.out.println(new RomanToDecimal().convertRomanToDecimal("XCIX"));
}
}
Full version with error checking and test all valid values in both directions (and some invalid cases).
RomanNumeral.java
import java.util.ArrayList;
import java.util.TreeMap;
/**
* Convert to and from a roman numeral string
*/
public class RomanNumeral {
// used for converting from arabic number
final static TreeMap<Integer, String> mapArabic = new TreeMap<Integer, String>();
// used for converting from roman numeral
final static ArrayList<RomanDigit> mapRoman = new ArrayList<RomanDigit>();
final static int MAX_ARABIC = 3999;
static {
mapArabic.put(1000, "M");
mapArabic.put(900, "CM");
mapArabic.put(500, "D");
mapArabic.put(400, "CD");
mapArabic.put(100, "C");
mapArabic.put(90, "XC");
mapArabic.put(50, "L");
mapArabic.put(40, "XL");
mapArabic.put(10, "X");
mapArabic.put(9, "IX");
mapArabic.put(5, "V");
mapArabic.put(4, "IV");
mapArabic.put(1, "I");
mapRoman.add(new RomanDigit("M", 1000, 3, 1000));
mapRoman.add(new RomanDigit("CM", 900, 1, 90));
mapRoman.add(new RomanDigit("D", 500, 1, 100));
mapRoman.add(new RomanDigit("CD", 400, 1, 90));
mapRoman.add(new RomanDigit("C", 100, 3, 100));
mapRoman.add(new RomanDigit("XC", 90, 1, 9));
mapRoman.add(new RomanDigit("L", 50, 1, 10));
mapRoman.add(new RomanDigit("XL", 40, 1, 9));
mapRoman.add(new RomanDigit("X", 10, 3, 10));
mapRoman.add(new RomanDigit("IX", 9, 1, 0));
mapRoman.add(new RomanDigit("V", 5, 1, 1));
mapRoman.add(new RomanDigit("IV", 4, 1, 0));
mapRoman.add(new RomanDigit("I", 1, 3, 1));
}
static final class RomanDigit {
public final String numeral;
public final int value;
public final int maxConsecutive;
public final int maxNextValue;
public RomanDigit(String numeral, int value, int maxConsecutive, int maxNextValue) {
this.numeral = numeral;
this.value = value;
this.maxConsecutive = maxConsecutive;
this.maxNextValue = maxNextValue;
}
}
/**
* Convert an arabic integer value into a roman numeral string
*
* #param n The arabic integer value
* #return The roman numeral string
*/
public final static String toRoman(int n) {
if (n < 1 || n > MAX_ARABIC) {
throw new NumberFormatException(String.format("Invalid arabic value: %d, must be > 0 and < %d", n, MAX_ARABIC));
}
int leDigit = mapArabic.floorKey(n);
//System.out.println("\t*** floor of " + n + " is " + leDigit);
if (n == leDigit) {
return mapArabic.get(leDigit);
}
return mapArabic.get(leDigit) + toRoman(n - leDigit);
}
/**
* Convert a roman numeral string into an arabic integer value
* #param s The roman numeral string
* #return The arabic integer value
*/
public final static int toInt(String s) throws NumberFormatException {
if (s == null || s.length() == 0) {
throw new NumberFormatException("Invalid roman numeral: a non-empty and non-null value must be given");
}
int i = 0;
int iconsecutive = 0; // number of consecutive same digits
int pos = 0;
int sum = 0;
RomanDigit prevDigit = null;
while (pos < s.length()) {
RomanDigit d = mapRoman.get(i);
if (!s.startsWith(mapRoman.get(i).numeral, pos)) {
i++;
// this is the only place we advance which digit we are checking,
// so if it exhausts the digits, then there is clearly a digit sequencing error or invalid digit
if (i == mapRoman.size()) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: invalid sequence '%s' following digit '%s'",
pos, s.substring(pos), prevDigit != null ? prevDigit.numeral : ""));
}
iconsecutive = 0;
continue;
}
// we now have the match for the next roman numeral digit to check
iconsecutive++;
if (iconsecutive > d.maxConsecutive) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: more than %d consecutive occurences of digit '%s'",
pos, d.maxConsecutive, d.numeral));
}
// invalid to encounter a higher digit sequence than the previous digit expects to have follow it
// (any digit is valid to start a roman numeral - i.e. when prevDigit == null)
if (prevDigit != null && prevDigit.maxNextValue < d.value) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: '%s' cannot follow '%s'",
pos, d.numeral, prevDigit.numeral));
}
// good to sum
sum += d.value;
if (sum > MAX_ARABIC) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: adding '%s' exceeds the max value of %d",
pos, d.numeral, MAX_ARABIC));
}
pos += d.numeral.length();
prevDigit = d;
}
return sum;
}
}
Main.java
public class Main {
public static void main(String[] args) {
System.out.println("TEST arabic integer => roman numeral string");
for (int i = 0; i<= 4000; i++) {
String s;
try {
s = RomanNumeral.toRoman(i);
}
catch(NumberFormatException ex) {
s = ex.getMessage();
}
System.out.println(i + "\t =\t " + s);
}
System.out.println("TEST roman numeral string => arabic integer");
for (int i = 0; i<= 4000; i++) {
String s;
String msg;
try {
s = RomanNumeral.toRoman(i);
int n = testToInt(s);
assert(i == n); // ensure it is reflexively converting
}
catch (NumberFormatException ex) {
System.out.println(ex.getMessage() + "\t =\t toInt() skipped");
}
}
testToInt("MMMM");
testToInt("XCX");
testToInt("CDC");
testToInt("IVI");
testToInt("XXC");
testToInt("CCD");
testToInt("MDD");
testToInt("DD");
testToInt("CLL");
testToInt("LL");
testToInt("IIX");
testToInt("IVX");
testToInt("IIXX");
testToInt("XCIX");
testToInt("XIWE");
// Check validity via a regexp for laughs
String s = "IX";
System.out.println(s + " validity is " + s.matches("M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})"));
}
private final static int testToInt(String s) {
String msg;
int n=0;
try {
n = RomanNumeral.toInt(s);
msg = Integer.toString(n);
}
catch(NullPointerException | NumberFormatException ex) {
msg = ex.getMessage();
}
System.out.println(s + "\t =\t " + msg);
return n;
}
}
Output
TEST arabic integer => roman numeral string
0 = Invalid arabic value: 0, must be > 0 and < 3999
1 = I
2 = II
3 = III
4 = IV
5 = V
6 = VI
7 = VII
8 = VIII
9 = IX
10 = X
... [snip] ...
3988 = MMMCMLXXXVIII
3989 = MMMCMLXXXIX
3990 = MMMCMXC
3991 = MMMCMXCI
3992 = MMMCMXCII
3993 = MMMCMXCIII
3994 = MMMCMXCIV
3995 = MMMCMXCV
3996 = MMMCMXCVI
3997 = MMMCMXCVII
3998 = MMMCMXCVIII
3999 = MMMCMXCIX
4000 = Invalid arabic value: 4000, must be > 0 and < 3999
TEST roman numeral string => arabic integer
Invalid arabic value: 0, must be > 0 and < 3999 = toInt() skipped
I = 1
II = 2
III = 3
IV = 4
V = 5
VI = 6
VII = 7
VIII = 8
IX = 9
X = 10
... [snip] ...
MMMCMLXXXVIII = 3988
MMMCMLXXXIX = 3989
MMMCMXC = 3990
MMMCMXCI = 3991
MMMCMXCII = 3992
MMMCMXCIII = 3993
MMMCMXCIV = 3994
MMMCMXCV = 3995
MMMCMXCVI = 3996
MMMCMXCVII = 3997
MMMCMXCVIII = 3998
MMMCMXCIX = 3999
Invalid arabic value: 4000, must be > 0 and < 3999 = toInt() skipped
MMMM = Invalid roman numeral at pos 3: more than 3 consecutive occurences of digit 'M'
XCX = Invalid roman numeral at pos 2: 'X' cannot follow 'XC'
CDC = Invalid roman numeral at pos 2: 'C' cannot follow 'CD'
IVI = Invalid roman numeral at pos 2: 'I' cannot follow 'IV'
XXC = Invalid roman numeral at pos 2: invalid sequence 'C' following digit 'X'
CCD = Invalid roman numeral at pos 2: invalid sequence 'D' following digit 'C'
MDD = Invalid roman numeral at pos 2: more than 1 consecutive occurences of digit 'D'
DD = Invalid roman numeral at pos 1: more than 1 consecutive occurences of digit 'D'
CLL = Invalid roman numeral at pos 2: more than 1 consecutive occurences of digit 'L'
LL = Invalid roman numeral at pos 1: more than 1 consecutive occurences of digit 'L'
IIX = Invalid roman numeral at pos 2: invalid sequence 'X' following digit 'I'
IVX = Invalid roman numeral at pos 2: invalid sequence 'X' following digit 'IV'
IIXX = Invalid roman numeral at pos 2: invalid sequence 'XX' following digit 'I'
XCIX = 99
XIWE = Invalid roman numeral at pos 2: invalid sequence 'WE' following digit 'I'
IX validity is true
Lets look at this problem with 3 different scenarios
Scenario 1:
When we see a pattern like the following
'IIIIII' or 'XXXXX' or 'CCCC'
where all characters as the same: We add the value of each characters in the pattern
'IIIIII' gives us '6'
'XXXXX' gives us '50'
'CCCC' gives us '400'
Scenario 2:
When we see any 2 consecutive characters different where first is smaller in value then the 2nd
'IX' or 'XC'
We subtract the value of first from second e.g.
second:'X' gives us '10'
first: 'I' gives us '1'
second - first : 10 - 1 = 9
Scenario 3:
When we see a any 2 consecutive characters different where first is greater in value then the second
'XI' or 'CX'
We add first and second e.g.
second:'I' gives us '10'
first: 'X' gives us '1'
first + second : 10 + 1 = 11
Now we can find the result if we do this recursively.
Here is the java implementation :
//An array to be used for faster comparisons and reading the values
private int[] letters26 = new int[26];
private void init () {
letters26['I' - 'A'] = 1;
letters26['V' - 'A'] = 5;
letters26['X' - 'A'] = 10;
letters26['L' - 'A'] = 50;
letters26['C' - 'A'] = 100;
letters26['D' - 'A'] = 500;
letters26['M' - 'A'] = 1000;
}
public int convertRomanToInteger(String s) {
//Initialize the array
init();
return _convertRomanToInteger(s.toCharArray(), 0);
}
//Recursively calls itself as long as 2 consecutive chars are different
private int _convertRomanToInteger(char[] s, int index) {
int ret = 0;
char pre = s[index];//Char from the given index
ret = _getValue(pre);
//Iterate through the rest of the string
for (int i = index + 1; i < s.length; i++) {
if (compare(s[i], s[i - 1]) == 0) {
//Scenario 1:
//If 2 consecutive chars are similar, just add them
ret += _getValue(s[i]);
} else if (compare(s[i], s[i - 1]) > 0) {
//Scenario 2:
//If current char is greater than the previous e.g IX ('I' s[i - 1] and 'X' s[i - 1])
//We need to calculate starting from 'i' and subtract the calculation ('ret')
//done so far in current call
return _convertRomanToInteger(s, i) - ret;
} else {
//Scenario 3:
//If current char is smaller than the previous e.g XI ('X' s[i - 1] and 'I' s[i - 1])
//We need to calculate starting from 'i' and subtract the result
//from the calculation done so far in current call
return ret + _convertRomanToInteger(s, i);
}
}
return ret;
}
//Helper function for comparison
private int compare(char a, char b) {
return letters26[Character.toUpperCase(a) - 'A']
- letters26[Character.toUpperCase(b) - 'A'];
}
private int _getValue(char c) {
return letters26[Character.toUpperCase(c) - 'A'];
}
Despite the fact that a lot of solutions have been already proposed.
I guess that the following one will be short and clear:
public class RomanToInteger {
public static int romanToInt(String roman) {
final Map<Character, Integer> map = Map.of('I', 1,
'V', 5,
'X', 10,
'L', 50,
'C', 100,
'D', 500,
'M', 1000);
int result = 0;
for (int index = 0; index < roman.length(); index++) {
final int curNumber = map.get(roman.charAt(index));
if (index > 0 && curNumber > map.get(roman.charAt(index - 1))) {
// add current number & remove previous one twice:
// first: we add it before (when it was current number) and removing it for this current number
// second: for correct conversion to roman numbers
result += curNumber - 2 * map.get(roman.charAt(index - 1));
} else {
result += curNumber;
}
}
System.out.printf("%8s -> %4d\n", roman, result);
return result;
}
public static void main(String[] args) {
String[] romans = {"I", "II", "III", "V", "X", "XIV", "XVII", "XX", "XXV",
"XXX", "XXXVIII", "XLIX", "LXIII", "LXXXI", "XCVII", "XCVIII", "XCIX",
"C", "CI", "CCXLVIII", "CCLIII", "DCCXCIX", "MCCCXXV", "MCM", "MM",
"MMCDLVI", "MDCCXV"};
final Instant startTimeIter = Instant.now();
for (String roman : romans) {
romanToInt(roman);
}
final Instant endTimeIter = Instant.now();
System.out.printf("Elapsed time: %d ms\n", Duration.between(startTimeIter, endTimeIter).toMillis());
}
}
Output:
I -> 1
II -> 2
III -> 3
...
MMCDLVI -> 2456
MDCCXV -> 1715
Elapsed time: 101 ms
Logic is quite simple we just go from left to right through roman literal:
if a literal isn't first and the previous one is lower -> we have to add current number to total & extract the previous number twice. First time what we added this at the previous step (when it was current number). Second because it is a rule of roman literals conversion (like IX literal).
otherwise, just add it to the total result.
How about this?
int getdec(const string& input)
{
int sum=0; char prev='%';
for(int i=(input.length()-1); i>=0; i--)
{
if(value(input[i])<sum && (input[i]!=prev))
{ sum -= value(input[i]);
prev = input[i];
}
else
{
sum += value(input[i]);
prev = input[i];
}
}
return sum;
}
Supposing Well-formed Roman numbers:
private static int totalValue(String val)
{
String aux=val.toUpperCase();
int sum=0, max=aux.length(), i=0;
while(i<max)
{
if ((i+1)<max && valueOf(aux.charAt(i+1))>valueOf(aux.charAt(i)))
{
sum+=valueOf(aux.charAt(i+1)) - valueOf(aux.charAt(i));
i+=2;
}
else
{
sum+=valueOf(aux.charAt(i));
i+=1;
}
}
return sum;
}
private static int valueOf(Character c)
{
char aux = Character.toUpperCase(c);
switch(aux)
{
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
what about this conversion. no switch, no case at all...
P.S. : I use this script from a bash shell
import sys
def RomanToNum(r):
return {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}[r]
#
#
#
EOF = "<"
Roman = sys.argv[1].upper().strip()+EOF
num = 0
i = 0
while True:
this = Roman[i]
if this == EOF:
break
n1 = RomanToNum(this)
next = Roman[i+1]
if next == EOF:
n2 = 0
else:
n2 = RomanToNum(next)
if n1 < n2:
n1 = -1 * n1
num = num + n1
i = i + 1
print num
public class RomInt {
String roman;
int val;
void assign(String k)
{
roman=k;
}
private class Literal
{
public char literal;
public int value;
public Literal(char literal, int value)
{
this.literal = literal;
this.value = value;
}
}
private final Literal[] ROMAN_LITERALS = new Literal[]
{
new Literal('I', 1),
new Literal('V', 5),
new Literal('X', 10),
new Literal('L', 50),
new Literal('C', 100),
new Literal('D', 500),
new Literal('M', 1000)
};
public int getVal(String s) {
int holdValue=0;
for (int j = 0; j < ROMAN_LITERALS.length; j++)
{
if (s.charAt(0)==ROMAN_LITERALS[j].literal)
{
holdValue=ROMAN_LITERALS[j].value;
break;
} //if()
}//for()
return holdValue;
} //getVal()
public int count()
{
int count=0;
int countA=0;
int countB=0;
int lastPosition = 0;
for(int i = 0 ; i < roman.length(); i++)
{
String s1 = roman.substring(i,i+1);
int a=getVal(s1);
countA+=a;
}
for(int j=1;j<roman.length();j++)
{
String s2= roman.substring(j,j+1);
String s3= roman.substring(j-1,j);
int b=getVal(s2);
int c=getVal(s3);
if(b>c)
{
countB+=c;
}
}
count=countA-(2*countB);
return count;
}
void disp()
{
int result=count();
System.out.println("Integer equivalent of "+roman+" = " +result);
}
} //RomInt---BLC
I find the following approach a very intuitive one:
public void makeArray(String romanNumeral){
int[] numberArray = new int[romanNumeral.length()];
for(int i=0; i<romanNumeral.length();i++){
char symbol = romanNumeral.charAt(i);
switch(symbol){
case 'I':
numberArray[i] = 1;
break;
case 'V':
numberArray[i] = 5;
break;
case 'X':
numberArray[i] = 10;
break;
case 'L':
numberArray[i] = 50;
break;
case 'C':
numberArray[i] = 100;
break;
case 'D':
numberArray[i] = 500;
break;
case 'M':
numberArray[i] = 1000;
break;
}
}
calculate(numberArray);
}
public static void calculate(int[] numberArray){
int theNumber = 0;
for(int n=0;n<numberArray.length;n++){
if(n !=numberArray.length-1 && numberArray[n] < numberArray[n+1]){
numberArray[n+1] = numberArray[n+1] - numberArray[n];
numberArray[n] = 0;
}
}
for(int num:numberArray){
theNumber += num;
}
System.out.println("Converted number: " + theNumber);
}
//Bet no one has a smaller and easier logic than this........Open CHALLENGE!!!!!!!
import java.io.*;
class Convertion_practical_q2
{
void Decimal()throws IOException //Smaller code for convertion from roman to decimal
{
DataInputStream in=new DataInputStream(System.in);
System.out.println("Enter the number");
String num=in.readLine();
char pos[]={'0','I','V','X','L','C','D','M'};
int l1=7; //l1 is size of pos array
String v[]={"","1","5","10","50","100","500","1000"};
int l=num.length();
int p=0,p1=0,sum=0;
for(int i=l-1;i>=0;i--)
{
char ch=num.charAt(i);
for(int j=1;j<=l1;j++)
{
if(ch==pos[j])
p=j;
}
if(p>=p1)
sum+=Integer.parseInt(v[p]);
else
sum-=Integer.parseInt(v[p]);
//System.out.println("sum ="+sum+"\np="+p+"\np1="+p1);
p1=p;
}
System.out.println(sum);
}
}
Just got it working in Java, nice job guys.
public int getDecimal (String roman) {
int decimal = 0;
int romanNumber = 0;
int prev = 0;
for (int i = roman.length()-1; i >= 0; i--){
romanNumber = hashRomans.get(roman.charAt(i));
if(romanNumber < decimal && romanNumber != prev ){
decimal -= romanNumber;
prev = romanNumber;
} else {
decimal += romanNumber;
prev = romanNumber;
}
}
return decimal;
}
This should work:
import java.io.*;
import java.util.Scanner;
enum RomanToNumber {
i(1), v(5), x(10), l(50), c(100); int value;
RomanToNumber (int p){value = p;}
int getValue(){return value;}
}
public class RomanToInteger {
public static void main(String[] args){
RomanToNumber n;
System.out.println( "Type a valid roman number in lower case" );
String Str = new String(new Scanner(System.in).nextLine());
int n1 = 0, theNo = 0, len = Str.length();
int[] str2No = new int [len];
for(int i=0; i < len; i++){
n = RomanToNumber.valueOf(Str.substring(n1, ++n1));
str2No[i] = n.getValue();
}
for(int j = 0; j < (len-1); j++){
if( str2No[j] >= str2No[j+1] ){ theNo += str2No[j]; }
else{ theNo -= str2No[j]; }
}
System.out.println( theNo += str2No[len-1] );
}
}
Since most of the answers here are in Java, I'm posting the answer in C++ (since I am bored right now and nothing more productive to do :) But please, no downvotes except if code is wrong.
Known-issues = will not handle overflow
Code:
#include <unordered_map>
int convert_roman_2_int(string& str)
{
int ans = 0;
if( str.length() == 0 )
{
return ans;
}
std::unordered_map<char, int> table;
table['I'] = 1;
table['V'] = 5;
table['X'] = 10;
table['L'] = 50;
table['C'] = 100;
table['D'] = 500;
table['M'] = 1000;
ans = table[ str[ str.length() - 1 ] ];
for( int i = str.length() - 2; i >= 0; i--)
{
if(table[ str[i] ] < table[ str[i+1] ] )
{
ans -= table[ str[i] ];
}
else
{
ans += table[ str[i] ];
}
}
return ans;
}
// test code
void run_test_cases_convert_roman_to_int()
{
string roman = "VIII";
int r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "XX";
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "CDX"; //410
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MCMXC"; //1990
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MMVIII"; //2008
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MDCLXVI"; //1666
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
}
this is very basic implementation of what you asked and working for all the test cases. If you find anything wrong in it than do mention it,i will try to make it correct also the code is in C++ .
int RomanToInt(string s){
int len = s.length();
map<char,int>mp;
int decimal = 0;
mp['I'] = 1;mp['V'] = 5;mp['X'] = 10;
mp['L'] = 50;mp['C'] = 100;mp['D'] = 500;mp['M'] = 1000;
for(int i = 0; i < len ;i++){
char cur = s[i],fast = s[i+1];
int cur_val = mp[cur],fast_val = mp[fast];
if(cur_val < fast_val){
decimal = decimal - cur_val;
}else{
decimal += cur_val;
}
}
return decimal;
}
public static int convertFromRoman(String romanNumeral)
{
Character[] rnChars = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
int[] rnVals = { 1000, 500, 100, 50, 10, 5, 1 };
HashMap<Character, Integer> valueLookup = new HashMap<Character, Integer>();
for (int i = 0; i < rnChars.length;i++)
valueLookup.put(rnChars[i], rnVals[i]);
int retVal = 0;
for (int i = 0; i < romanNumeral.length();i++)
{
int addVal = valueLookup.get(romanNumeral.charAt(i));
retVal += i < romanNumeral.length()-1 &&
addVal < valueLookup.get(romanNumeral.charAt(i+1))?
-addVal:addVal;
}
return retVal;
}
This is a modest variation of the recursive algorithm suggested by Sahtiel:
public static int toArabic(String number) throws Exception {
String[] letras = {"M","CM","D","CD","C","XC","L","XL","X", "IX","V","IV","I"};
int[] valores = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
// here we can do even more business validations like avoiding sequences like XXXXM
if (number==null || number.isEmpty()) {
return 0;
}
for(int i=0; i<letras.length; i++) {
if (number.startsWith(letras[i])) {
return valores[i] + toArabic(number.substring(letras[i].length()));
}
}
throw new Exception("something bad happened");
}
It uses less than 10 effective lines of code.
This scala solution might be useful:
class RomanNumberConverter {
private val toArabic = Map('I' -> 1, 'V' -> 5, 'X' -> 10, 'L' -> 50, 'C' -> 100, 'D' -> 500, 'M' -> 1000)
// assume that correct roman number is provided
def convert(romanNumber: String): Int = {
def convert(rn: StringBuilder, lastDecimal: Int, acc: Int): Int = {
if (rn.isEmpty) acc
else {
val thisDecimal = toArabic(rn.head)
if (thisDecimal > lastDecimal) convert(rn.tail, thisDecimal, acc + thisDecimal - lastDecimal - lastDecimal)
else convert(rn.tail, thisDecimal, acc + thisDecimal)
}
}
val sb = new StringBuilder(romanNumber)
convert(sb.tail, toArabic(sb.head), toArabic(sb.head))
}
}
Solution using tail recursion:
import java.util.LinkedHashMap;
public class RomanNumber {
private final static LinkedHashMap<String, Integer> roman2number = new LinkedHashMap<>(); // preserve key order
static {
roman2number.put("M", 1000);
roman2number.put("CM", 900);
roman2number.put("D", 500);
roman2number.put("CD", 400);
roman2number.put("C", 100);
roman2number.put("XC", 90);
roman2number.put("L", 50);
roman2number.put("XL", 40);
roman2number.put("X", 10);
roman2number.put("IX", 9);
roman2number.put("V", 5);
roman2number.put("IV", 4);
roman2number.put("I", 1);
}
public final static Integer toDecimal(String roman) {
for (String key : roman2number.keySet()) {
if (roman.startsWith(key)) {
if (roman.equals(key)) {
return roman2number.get(key);
}
return roman2number.get(key) + toDecimal(roman.substring(key.length()));
}
}
return 0;
}
}
Testing:
import junitparams.JUnitParamsRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.Parameters;
import static org.junit.Assert.assertTrue;
#RunWith(JUnitParamsRunner.class)
public class RomanNumberTest {
#Test
#Parameters({ "1|I", "2|II", "3|III", "4|IV", "5|V", "6|VI", "7|VII", "8|VIII", "9|IX", "10|X",
"11|XI", "12|XII", "13|XIII", "14|XIV", "15|XV", "16|XVI", "17|XVII", "18|XVIII", "19|XIX",
"20|XX", "50|L", "53|LIII", "57|LVII", "40|XL", "49|XLIX", "59|LIX", "79|LXXIX", "100|C", "90|XC", "99|XCIX",
"200|CC", "500|D", "499|CDXCIX", "999|CMXCIX", "2999|MMCMXCIX", "3999|MMMCMXCIX"
})
public void forRomanReturnsNumber(int number, String roman) {
assertTrue(roman + "->" + number, RomanNumber.toDecimal(roman) == (number));
}
}
With only two if-statements:
Check the match of first character on priority and if not matching, match the single first character.
public class Solution {
private static TreeMap<String, Integer> numeralsAndVal;
static{
numeralsAndVal = new TreeMap<>();
numeralsAndVal.put("M", 1000);
numeralsAndVal.put("CM", 900);
numeralsAndVal.put("D", 500);
numeralsAndVal.put("CD", 400);
numeralsAndVal.put("C", 100);
numeralsAndVal.put("XC", 90);
numeralsAndVal.put("L", 50);
numeralsAndVal.put("XL", 40);
numeralsAndVal.put("X", 10);
numeralsAndVal.put("IX", 9);
numeralsAndVal.put("V", 5);
numeralsAndVal.put("IV", 4);
numeralsAndVal.put("I", 1);
}
public int romanToInt(String A) {
if (A.length() == 0) return 0;
if (A.length() >= 2 && numeralsAndVal.containsKey(A.substring(0, 2))){
return numeralsAndVal.get(A.substring(0, 2)) + romanToInt(A.substring(2));
}
return numeralsAndVal.get(A.substring(0, 1)) + romanToInt(A.substring(1));
}
}
For converting TO arabic, it works as follows:
grab the rightmost unchecked character and convert it to its numeric value using the map. If this value is less than the "top" value we've seen so far, then it needs to be subtracting (e.g. IV needs to be 4, not 6), otherwise we add the value to the total and make it the new top value we've seen so far. So, for instance, XIX works like follows: 'X' is checked and comes out to be value of 10. That is higher than the "top" value seen so far, so it becomes "top" and we add 10 to the value. Then go to the next character and "I" is < 10, so subtract
public class RomanNumeral {
private final Map<Integer, String> arabicToRoman = new LinkedHashMap<Integer, String>();
private final Map<String, Integer> romanToArabic = new LinkedHashMap<String, Integer>();
public RomanNumeral() {
arabicToRoman.put(10, "X");
arabicToRoman.put(9, "IX");
arabicToRoman.put(5, "V");
arabicToRoman.put(4, "IV");
arabicToRoman.put(1, "I");
romanToArabic.put("X", 10);
romanToArabic.put("V", 5);
romanToArabic.put("I", 1);
}
public String convertToRomanNumeral(int number) {
String result = "";
for (Integer i : arabicToRoman.keySet()) {
while (number >= i) {
result += arabicToRoman.get(i);
number -= i;
}
}
return result;
}
public String convertToArabicNumber(String romanNumeral) {
int result = 0;
int top = 0;
for (int i = romanNumeral.length() - 1; i >= 0; i--) {
char current = romanNumeral.charAt(i);
int value = romanToArabic.get("" + current);
if (value < top) {
result -= value;
} else {
result += value;
top = value;
}
}
return "" + result;
}
}
An easy solution for input in the range from 1 to 3999.
private static final Map<String, Integer> MAPPING = new HashMap<>() {{
put("I", 1);
put("V", 5);
put("X", 10);
put("L", 50);
put("C", 100);
put("D", 500);
put("M", 1000);
put("IV", 4);
put("IX", 9);
put("XL", 40);
put("XC", 90);
put("CD", 400);
put("CM", 900);
}};
public static int romanToInt(String s) {
int sum = 0;
for (int i = 0; i < s.length(); i++) {
if (i < s.length() - 1 && MAPPING.containsKey(String.valueOf(s.charAt(i)) + s.charAt(i + 1))) {
sum += MAPPING.get(String.valueOf(s.charAt(i)) + s.charAt(i + 1));
i++;
} else {
sum += MAPPING.get(String.valueOf(s.charAt(i)));
}
}
return sum;
}
The problem here is that numbers like 9 are presented in the following way
IX = 9
if we take the literal conversion by getting the sum for each number than our program would give us the following sum
I = 1
X = 10
X + I = 11
which is not correct.
We can overcome this if we simply manipulate the Roman String.
In example:
IX = 9 would be VIIII which is 5 + 1 + 1 + 1 + 1
Therefore if we replace all the special cases in the given String, we can calculate the sum for each character.
Hence;
class Solution {
public int romanToInt(String s) {
// Key Value pairs
Map<Character, Integer> map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
// convert these to single characters
s = s.replace("IV","IIII");
s = s.replace("IX","VIIII");
s = s.replace("XL","XXXX");
s = s.replace("XC","LXXXX");
s = s.replace("CD","CCCC");
s = s.replace("CM","DCCCC");
// total sum to be returned
int sum = 0;
// convert s to c character array
char[] c = s.toCharArray();
// iterate through c array and sum the numbers
for(Character ch : c){
sum = sum + map.get(ch);
}
return sum;
}
}
Here is my one...
import java.util.Scanner;
class Solution {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
String num;
// System.out.println("enter the number you want to convert from roman to integer.");
num = "D";
System.out.println("length of string is " + num.length());
System.out.println("the integer number is :" + romanToInt(num) );
}
public static int romanToInt(String s) {
char I,V, X, L,C, D, M;
char c1,c3, c2 = s.charAt(0);
// System.out.println("the c2 is : " + (int)c2);
int num = 0, num1 = 0;
int j =0, k = 0, temp = 0;
if (c2 == 'I') {
k = (int)c2 - 72;
System.out.println("k is I" + k);
} else if(c2 == 'V') {
k = (int)c2 - 81;
System.out.println("K is V" + k);
// return 86 - 81;
} else if (c2 == 'X') {
k = (int)c2 - 78;
System.out.println("K is X" + k);
} else if (c2 == 'L') {
k = (int)c2 - 26;
System.out.println("K is L" + k);
} else if (c2 == 'C') {
k = (int)c2 + 33;
System.out.println("K is C" + k);
} else if (c2 == 'D') {
k = (int)c2 + 432;
System.out.println("K is D" + k);
} else if ( c2 == 'M') {
k = (int)c2 + 923;
System.out.println("K is M" + k);
}
if (s.length() == 1){
num = k;
} else {
for(int i = 1; i<= s.length()-1 ; i++) {
System.out.println("i is : " + i);
c1 = s.charAt(i);
if (i == s.length() - 1) {
temp = 0;
} else {
c3 = s.charAt(i+1);
if (c3 == 'I') {
temp = (int)c3 - 72;
System.out.println("temp is I " + temp);
} else if(c3 == 'V') {
temp = (int)c3 - 81;
System.out.println("temp is I " + temp);
// return 86 - 81;
} else if (c3 == 'X') {
temp = (int)c3 - 78;
System.out.println("temp is I " + temp);
} else if (c3 == 'L') {
temp = (int)c3 - 26;
System.out.println("temp is I " + temp);
} else if (c3 == 'C') {
temp = (int)c3 + 33;
System.out.println("temp is I " + temp);
} else if (c3 == 'D') {
temp = (int)c3 + 432;
System.out.println("temp is I " + temp);
} else if ( c3 == 'M') {
temp = (int)c3 + 923;
System.out.println("temp is I " + temp);
}
}
if (c1 == 'I') {
j = (int)c1 - 72;
System.out.println("j is I " + j);
} else if(c1 == 'V') {
j = (int)c1 - 81;
System.out.println("j is V " + j);
// return 86 - 81;
} else if (c1 == 'X') {
j = (int)c1 - 78;
System.out.println("j is X " + j);
} else if (c1 == 'L') {
j = (int)c1 - 26;
System.out.println("j is L " + j);
} else if (c1 == 'C') {
j = (int)c1 + 33;
System.out.println("j is C " + j);
} else if (c1 == 'D') {
j = (int)c1 + 432;
System.out.println("j is D " + j);
} else if ( c1 == 'M') {
j = (int)c1 + 923;
System.out.println("j is M " + j);
}
if ( k < j && j>temp ) {
k = j - k ;
num = num + k;
} else if (j==k || j<k || j<temp){
num = num + k ;
// k = j;
}
if (j>k ) {
k = temp;
i += 1;
if (i == s.length()-1) {
num = num + k;
}
} else {
k = j;
if (i == s.length()-1) {
num = num + k;
}
}
}
}
return num;
}
}

Categories