วันพุธที่ 28 พฤษภาคม พ.ศ. 2551

Roman Numerals

Roman numerals is a numeral system used in ancient Rome. However, today we still use Roman numerals mostly for number-lists, places' year, and on the watches. Here are the letters, which represent a number.

"I" is for 1
"V" is for 5
"X" is for 10
"L" is for 50
"C" is for 100
"D" is for 500
"M" is for 1000

Rule for reading Roman numerals is simple, just sum up value of each letter. However, there is an exception for reduce form. If value of the letter on the left is less than value of the letter on the right, that is reduce form. That letter must deduct from the sum instead of addition. Such as "XLIX", first X is less than L, and I is less than second X. We can write it as -10 + 50 - 1 + 10 = 49.

Rule for writing Roman numerals is also simple. Just deduct from sum by using largest value letter as possible. And you cannot using the letters for 4 times consecutively, in this case, you must write the reduce form. For example 19, Roman number should be "XVIIII" (10 + 5 + 1 + 1 + 1 + 1). But we cannot using I for 4 times consecutively, therefore "VIIII", must be written in reduce form as "IX". As a result, 19 can be written as "XIX".

Now, for coding.

public class Roman {
    int _value;

    public int Value {
        get { return _value; }
    }

    public Roman(int value) {
        if (value < 1 || value > 4999)
            throw new ArgumentOutOfRangeException("value", "value should be between 1 and 4999");
        _value = value;
    }

    public static implicit operator Roman(int x) {
        return new Roman(x);
    }
    public static Roman Parse(string roman) {
        var letter = "IVXLCDM";
        var num = new[] { 1, 5, 10, 50, 100, 500, 1000 };
        var dict = num.WithIndex(letter);

        int value, last = 0, sum = 0;
        foreach (char c in roman) {
            if (!dict.TryGetValue(c, out value))
                throw new ArgumentException("roman contains invalid characters", "roman");
            if (last < value)
                sum -= last;
            else
                sum += last;
            last = value;
        }
        sum += last;
        return new Roman(sum);
    }
    public override string ToString() {
        string numStr = _value.ToString("0000");
        string one = "MCXI";
        string[] five = { "MMM", "D", "L", "V" };
        StringBuilder sb = new StringBuilder();

        numStr.ForEach((c, i) => {
            if (c < '4')
                sb.Append(one[i], (int)(c - '0'));
            else if (c == '4')
                sb.Append(one[i]).Append(five[i]);
            else if (c < '9')
                sb.Append(five[i]).Append(one[i], (int)(c - '5'));
            else //9
                sb.Append(one[i]).Append(one[i - 1]);
        });
        return sb.ToString();
    }
}


If you notice, Parse method uses index injection from previous post, to transform 2 set of array into a dictionary.

To create a Roman number, just assign from integer.

Roman r = 19;

To read Roman number, use Parse method.

Roman r = Roman.Parse("XIX");

To write Roman number, use ToString method.

Console.WriteLine(r.ToString());
//XIX


To get value from Roman number, use Value property.

Console.WriteLine(r.Value);
//19


More example from CodeGolf. Roman to decimal number.

Roman r = Roman.Parse("MCDXL");
Console.WriteLine(r.Value);
//1440


Next article, start a new series topic "Dynamic Programming".

1 ความคิดเห็น:

Unknown กล่าวว่า...

I just love your articles at first sight. Thanks for sharing your knowledge.