Converting integers to roman numerals
Solution 1:
Try this, simple and compact:
public static string ToRoman(int number)
{
if ((number < 0) || (number > 3999)) throw new ArgumentOutOfRangeException("insert value betwheen 1 and 3999");
if (number < 1) return string.Empty;
if (number >= 1000) return "M" + ToRoman(number - 1000);
if (number >= 900) return "CM" + ToRoman(number - 900);
if (number >= 500) return "D" + ToRoman(number - 500);
if (number >= 400) return "CD" + ToRoman(number - 400);
if (number >= 100) return "C" + ToRoman(number - 100);
if (number >= 90) return "XC" + ToRoman(number - 90);
if (number >= 50) return "L" + ToRoman(number - 50);
if (number >= 40) return "XL" + ToRoman(number - 40);
if (number >= 10) return "X" + ToRoman(number - 10);
if (number >= 9) return "IX" + ToRoman(number - 9);
if (number >= 5) return "V" + ToRoman(number - 5);
if (number >= 4) return "IV" + ToRoman(number - 4);
if (number >= 1) return "I" + ToRoman(number - 1);
throw new ArgumentOutOfRangeException("something bad happened");
}
Solution 2:
I've created this class that does decimal <=> roman
public static class Roman
{
public static readonly Dictionary<char, int> RomanNumberDictionary;
public static readonly Dictionary<int, string> NumberRomanDictionary;
static Roman()
{
RomanNumberDictionary = new Dictionary<char, int>
{
{ 'I', 1 },
{ 'V', 5 },
{ 'X', 10 },
{ 'L', 50 },
{ 'C', 100 },
{ 'D', 500 },
{ 'M', 1000 },
};
NumberRomanDictionary = new Dictionary<int, 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" },
};
}
public static string To(int number)
{
var roman = new StringBuilder();
foreach (var item in NumberRomanDictionary)
{
while (number >= item.Key)
{
roman.Append(item.Value);
number -= item.Key;
}
}
return roman.ToString();
}
public static int From(string roman)
{
int total = 0;
int current, previous = 0;
char currentRoman, previousRoman = '\0';
for (int i = 0; i < roman.Length; i++)
{
currentRoman = roman[i];
previous = previousRoman != '\0' ? RomanNumberDictionary[previousRoman] : '\0';
current = RomanNumberDictionary[currentRoman];
if (previous != 0 && current > previous)
{
total = total - (2 * previous) + current;
}
else
{
total += current;
}
previousRoman = currentRoman;
}
return total;
}
}
Some Unit Tests for To
method:
[TestClass]
public class DecimalToRomanTest
{
[TestMethod]
public void Roman_1_I()
{
Assert.AreEqual("I", Roman.To(1));
}
[TestMethod]
public void Roman_2_II()
{
Assert.AreEqual("II", Roman.To(2));
}
[TestMethod]
public void Roman_3_III()
{
Assert.AreEqual("III", Roman.To(3));
}
[TestMethod]
public void Roman_4_IV()
{
Assert.AreEqual("IV", Roman.To(4));
}
[TestMethod]
public void Roman_5_V()
{
Assert.AreEqual("V", Roman.To(5));
}
[TestMethod]
public void Roman_9_IX()
{
Assert.AreEqual("IX", Roman.To(9));
}
[TestMethod]
public void Roman_10_X()
{
Assert.AreEqual("X", Roman.To(10));
}
[TestMethod]
public void Roman_49_XLIX()
{
Assert.AreEqual("XLIX", Roman.To(49));
}
[TestMethod]
public void Roman_50_L()
{
Assert.AreEqual("L", Roman.To(50));
}
[TestMethod]
public void Roman_100_C()
{
Assert.AreEqual("C", Roman.To(100));
}
[TestMethod]
public void Roman_400_CD()
{
Assert.AreEqual("CD", Roman.To(400));
}
[TestMethod]
public void Roman_500_D()
{
Assert.AreEqual("D", Roman.To(500));
}
[TestMethod]
public void Roman_900_CM()
{
Assert.AreEqual("CM", Roman.To(900));
}
[TestMethod]
public void Roman_1000_M()
{
Assert.AreEqual("M", Roman.To(1000));
}
[TestMethod]
public void Roman_11984_MMMMMMMMMMMCMLXXXIV()
{
Assert.AreEqual("MMMMMMMMMMMCMLXXXIV", Roman.To(11984));
}
}
Some Unit Tests for From
method:
[TestClass]
public class RomanToDecimalTest
{
[TestMethod]
public void Roman_I_1()
{
Assert.AreEqual(1, Roman.From("I"));
}
[TestMethod]
public void Roman_II_2()
{
Assert.AreEqual(2, Roman.From("II"));
}
[TestMethod]
public void Roman_III_3()
{
Assert.AreEqual(3, Roman.From("III"));
}
[TestMethod]
public void Roman_IV_4()
{
Assert.AreEqual(4, Roman.From("IV"));
}
[TestMethod]
public void Roman_V_5()
{
Assert.AreEqual(5, Roman.From("V"));
}
[TestMethod]
public void Roman_IX_9()
{
Assert.AreEqual(9, Roman.From("IX"));
}
[TestMethod]
public void Roman_X_10()
{
Assert.AreEqual(10, Roman.From("X"));
}
[TestMethod]
public void Roman_XLIX_49()
{
Assert.AreEqual(49, Roman.From("XLIX"));
}
[TestMethod]
public void Roman_L_50()
{
Assert.AreEqual(50, Roman.From("L"));
}
[TestMethod]
public void Roman_C_100()
{
Assert.AreEqual(100, Roman.From("C"));
}
[TestMethod]
public void Roman_CD_400()
{
Assert.AreEqual(400, Roman.From("CD"));
}
[TestMethod]
public void Roman_D_500()
{
Assert.AreEqual(500, Roman.From("D"));
}
[TestMethod]
public void Roman_CM_900()
{
Assert.AreEqual(900, Roman.From("CM"));
}
[TestMethod]
public void Roman_M_1000()
{
Assert.AreEqual(1000, Roman.From("M"));
}
[TestMethod]
public void Roman_MMMMMMMMMMMCMLXXXIV_11984()
{
Assert.AreEqual(11984, Roman.From("MMMMMMMMMMMCMLXXXIV"));
}
}
Solution 3:
Here's a much simpler algorithm - forgive me, I don't know C# so I'm writing this in JavaScript, but the same algorithm should apply (and I've commented so you can understand the algorithm):
function intToRoman(int) {
// create 2-dimensional array, each inner array containing
// roman numeral representations of 1-9 in each respective
// place (ones, tens, hundreds, etc...currently this handles
// integers from 1-3999, but could be easily extended)
var romanNumerals = [
['', 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix'], // ones
['', 'x', 'xx', 'xxx', 'xl', 'l', 'lx', 'lxx', 'lxxx', 'xc'], // tens
['', 'c', 'cc', 'ccc', 'cd', 'd', 'dc', 'dcc', 'dccc', 'cm'], // hundreds
['', 'm', 'mm', 'mmm'] // thousands
];
// split integer string into array and reverse array
var intArr = int.toString().split('').reverse(),
len = intArr.length,
romanNumeral = '',
i = len;
// starting with the highest place (for 3046, it would be the thousands
// place, or 3), get the roman numeral representation for that place
// and append it to the final roman numeral string
while (i--) {
romanNumeral += romanNumerals[ i ][ intArr[i] ];
}
return romanNumeral;
}
console.log( intToRoman(3046) ); // outputs mmmxlvi
Solution 4:
This is actually quite a fun problem, and based on the reverse example on dofactory.com (turning roman numerals to decimals) its quite easy to reverse the pattern, and perhaps improve it a little. This code will support numbers from 1 to 3999999.
Begin with a context class, this defines the I/O of the parser
public class Context
{
private int _input;
private string _output;
public Context(int input)
{
this._input = input;
}
public int Input
{
get { return _input; }
set { _input = value; }
}
public string Output
{
get { return _output; }
set { _output = value; }
}
}
And an abstract expression, which defines the parsing operation
public abstract class Expression
{
public abstract void Interpret(Context value);
}
Now, you need an abstract terminal expression, which defines the actual operation that will be performed:
public abstract class TerminalExpression : Expression
{
public override void Interpret(Context value)
{
while (value.Input - 9 * Multiplier() >= 0)
{
value.Output += Nine();
value.Input -= 9 * Multiplier();
}
while (value.Input - 5 * Multiplier() >= 0)
{
value.Output += Five();
value.Input -= 5 * Multiplier();
}
while (value.Input - 4 * Multiplier() >= 0)
{
value.Output += Four();
value.Input -= 4 * Multiplier();
}
while (value.Input - Multiplier() >= 0)
{
value.Output += One();
value.Input -= Multiplier();
}
}
public abstract string One();
public abstract string Four();
public abstract string Five();
public abstract string Nine();
public abstract int Multiplier();
}
Then, classes which define the behaviour of roman numerals (note, ive used the convention of lowercase where roman numerals use a bar over the letter to denote 1000 times)
class MillionExpression : TerminalExpression
{
public override string One() { return "m"; }
public override string Four() { return ""; }
public override string Five() { return ""; }
public override string Nine() { return ""; }
public override int Multiplier() { return 1000000; }
}
class HundredThousandExpression : TerminalExpression
{
public override string One() { return "c"; }
public override string Four() { return "cd"; }
public override string Five() { return "d"; }
public override string Nine() { return "cm"; }
public override int Multiplier() { return 100000; }
}
class ThousandExpression : TerminalExpression
{
public override string One() { return "M"; }
public override string Four() { return "Mv"; }
public override string Five() { return "v"; }
public override string Nine() { return "Mx"; }
public override int Multiplier() { return 1000; }
}
class HundredExpression : TerminalExpression
{
public override string One() { return "C"; }
public override string Four() { return "CD"; }
public override string Five() { return "D"; }
public override string Nine() { return "CM"; }
public override int Multiplier() { return 100; }
}
class TenExpression : TerminalExpression
{
public override string One() { return "X"; }
public override string Four() { return "XL"; }
public override string Five() { return "L"; }
public override string Nine() { return "XC"; }
public override int Multiplier() { return 10; }
}
class OneExpression : TerminalExpression
{
public override string One() { return "I"; }
public override string Four() { return "IV"; }
public override string Five() { return "V"; }
public override string Nine() { return "IX"; }
public override int Multiplier() { return 1; }
}
Almost there, we need a Non-terminal expression which contains the parse tree:
public class DecimalToRomaNumeralParser : Expression
{
private List<Expression> expressionTree = new List<Expression>()
{
new MillionExpression(),
new HundredThousandExpression(),
new TenThousandExpression(),
new ThousandExpression(),
new HundredExpression(),
new TenExpression(),
new OneExpression()
};
public override void Interpret(Context value)
{
foreach (Expression exp in expressionTree)
{
exp.Interpret(value);
}
}
}
Lastly, the client code:
Context ctx = new Context(123);
var parser = new DecimalToRomaNumeralParser();
parser.Interpret(ctx);
Console.WriteLine(ctx.Output); // Outputs CXXIII
Live example: http://rextester.com/rundotnet?code=JJBYW89744