Converting integers to roman numerals

前端 未结 29 2299
走了就别回头了
走了就别回头了 2020-12-02 09:16

I\'m trying to write a function that converts numbers to roman numerals. This is my code so far; however, it only works with numbers that are less than 400. Is there a quick

相关标签:
29条回答
  • 2020-12-02 09:56

    I gave it a try and my solution looks like this:

    public class RomanNumeral
    {
        private readonly IDictionary<int, string> romanDictionary = new Dictionary<int, string> 
        {
            {1, "I"}, {5, "V"}, {10, "X"}, {50, "L"}, {100, "C"}, {500, "D"}, {1000, "M"}
        };
    
        private int factor = 1;
    
        public string Parse(int arabicNumber)
        {
            if (arabicNumber < 0) throw new ArgumentException();
    
            var romanNumerals = new List<string>();
            foreach (var number in arabicNumber.Split().Reverse())
            {
                romanNumerals.Insert(0, ToRoman(number));
                factor *= 10;
            }
            return romanNumerals.Concatenated();
        }
    
        private string ToRoman(int number)
        {
            if (number.In(4, 9)) return ToRoman(1) + ToRoman(number + 1);
            if (number.In(6, 7, 8)) return ToRoman(number - 1) + ToRoman(1);
            if (number.In(2, 3)) return ToRoman(1) + ToRoman(number - 1);
            if (number == 0) return string.Empty;
            return romanDictionary[number * factor];
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:56

    Solution with fulfilling the "subtractive notation" semantics checks

    None of the current solutions completely fulfills the entire set of rules for the "subtractive notation". "IIII" -> is not possible. Each of the solutions results a 4. Also the strings: "CCCC", "VV", "IC", "IM" are invalid.

    A good online-converter to check the semantics is https://www.romannumerals.org/converter So, if you really want doing a completely semantics-check, it's much more complex.

    public class RomanNumerals
    {
    
        private List<Tuple<char, ushort, char?[]>> _validNumerals = new List<Tuple<char, ushort, char?[]>>()
        {
            new Tuple<char, ushort, char?[]>('I', 1, new char? [] {'V', 'X'}),
            new Tuple<char, ushort, char?[]>('V', 5, null),
            new Tuple<char, ushort, char?[]>('X', 10, new char?[] {'L', 'C'}),
            new Tuple<char, ushort, char?[]>('L', 50, null),
            new Tuple<char, ushort, char?[]>('C', 100, new char? [] {'D', 'M'}),
            new Tuple<char, ushort, char?[]>('D', 500, null),
            new Tuple<char, ushort, char?[]>('M', 1000, new char? [] {null, null})
        };
    
    
        public int TranslateRomanNumeral(string input)
        {
            var inputList = input?.ToUpper().ToList();
    
            if (inputList == null || inputList.Any(x => _validNumerals.Select(t => t.Item1).Contains(x) == false))
            {
                throw new ArgumentException();
            }
    
            char? valForSubtraction = null;
            int result = 0;
            bool noAdding = false;
            int equalSum = 0;
            for (int i = 0; i < inputList.Count; i++)
            {
                var currentNumeral = _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i]);
                var nextNumeral = i < inputList.Count - 1 ? _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i + 1]) : null;
                bool currentIsDecimalPower = currentNumeral?.Item3?.Any() ?? false;
    
                if (nextNumeral != null)
                {
                    // Syntax and Semantics checks
                    if ((currentNumeral.Item2 < nextNumeral.Item2) && (currentIsDecimalPower == false || currentNumeral.Item3.Any(s => s == nextNumeral.Item1) == false) ||
                        (currentNumeral.Item2 == nextNumeral.Item2) && (currentIsDecimalPower == false || nextNumeral.Item1 == valForSubtraction) ||
                        (currentIsDecimalPower && result > 0 &&  ((nextNumeral.Item2 -currentNumeral.Item2) > result )) ||
                        (currentNumeral.Item2 > nextNumeral.Item2) && (nextNumeral.Item1 == valForSubtraction)
    
                        )
                    {
                        throw new ArgumentException();
                    }
    
                    if (currentNumeral.Item2 == nextNumeral.Item2)
                    {
                        equalSum += equalSum == 0 ? currentNumeral.Item2 + nextNumeral.Item2 : nextNumeral.Item2;
                        int? smallest = null;
                        var list = _validNumerals.Where(p => _validNumerals.FirstOrDefault(s => s.Item1 == currentNumeral.Item1).Item3.Any(s2 => s2 != null && s2 == p.Item1)).ToList();
                        if (list.Any())
                        {
                            smallest = list.Select(s3 => s3.Item2).ToList().Min();
                        }
    
                        // Another Semantics check
                        if (currentNumeral.Item3 != null && equalSum >= (smallest - currentNumeral.Item2))
                        {
                            throw new ArgumentException();
                        }
    
                        result += noAdding ? 0 : currentNumeral.Item2 + nextNumeral.Item2;
                        noAdding = !noAdding;
                        valForSubtraction = null;
                    }
                    else
                    if (currentNumeral.Item2 < nextNumeral.Item2)
                    {
                        equalSum = 0;
                        result += nextNumeral.Item2 - currentNumeral.Item2;
                        valForSubtraction = currentNumeral.Item1;
                        noAdding = true;
                    }
                    else 
                    if (currentNumeral.Item2 > nextNumeral.Item2)
                    {
                        equalSum = 0;
                        result += noAdding ? 0 : currentNumeral.Item2;
                        noAdding = false;
    
                        valForSubtraction = null;
                    }
                }
                else
                {
                    result += noAdding ? 0 : currentNumeral.Item2;
                }
            }
            return result;
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:57

    While I liked Mosè Bottacini's answer, using recursion has a couple of negative side effects in this scenario. One being the possible stack overflow, hence his limiting of the upper bound of the number. While, yes, I realize how ridiculous a huge number looks in roman numerals, this is still a limitation that is not necessary to achieve the result.

    Also, since strings are immutable, his version is going to be very memory inefficient, due to the heavy use of string concatenation. Below is my modified version of his method, using just a while loop and a StringBuilder. My version should actually be more performant (although we're talking about differences in the sub-millisecond range) and much easier on system memory.

    public static string ToRomanNumeral(this int value)
    {
        if (value < 0)
            throw new ArgumentOutOfRangeException("Please use a positive integer greater than zero.");
    
        StringBuilder sb = new StringBuilder();
        int remain = value;
        while (remain > 0)
        {
            if (remain >= 1000) { sb.Append("M"); remain -= 1000; }
            else if (remain >= 900) { sb.Append("CM"); remain -= 900; }
            else if (remain >= 500) { sb.Append("D"); remain -= 500; }
            else if (remain >= 400) { sb.Append("CD"); remain -= 400; }
            else if (remain >= 100) { sb.Append("C"); remain -= 100; }
            else if (remain >= 90) { sb.Append("XC"); remain -= 90; }
            else if (remain >= 50) { sb.Append("L"); remain -= 50; }
            else if (remain >= 40) { sb.Append("XL"); remain -= 40; }
            else if (remain >= 10) { sb.Append("X"); remain -= 10; }
            else if (remain >= 9) { sb.Append("IX"); remain -= 9; }
            else if (remain >= 5) { sb.Append("V"); remain -= 5; }
            else if (remain >= 4) { sb.Append("IV"); remain -= 4; }
            else if (remain >= 1) { sb.Append("I"); remain -= 1; }
            else throw new Exception("Unexpected error."); // <<-- shouldn't be possble to get here, but it ensures that we will never have an infinite loop (in case the computer is on crack that day).
        }
    
        return sb.ToString();
    }
    
    0 讨论(0)
  • 2020-12-02 09:57

    A string representation of the number's corresponding roman numeral.

        public static string ToRomanNumeral(this int number)
        {
    
            var retVal = new StringBuilder(5);
            var valueMap = new SortedDictionary<int, string>
                               {
                                   { 1, "I" },
                                   { 4, "IV" },
                                   { 5, "V" },
                                   { 9, "IX" },
                                   { 10, "X" },
                                   { 40, "XL" },
                                   { 50, "L" },
                                   { 90, "XC" },
                                   { 100, "C" },
                                   { 400, "CD" },
                                   { 500, "D" },
                                   { 900, "CM" },
                                   { 1000, "M" },
                               };
    
            foreach (var kvp in valueMap.Reverse())
            {
                while (number >= kvp.Key)
                {
                    number -= kvp.Key;
                    retVal.Append(kvp.Value);
                }
            }
    
            return retVal.ToString();
        }
    
    0 讨论(0)
  • 2020-12-02 09:57
    public static String convert(int num)
    {
        String[] charsArray  = {"I", "IV", "V", "IX", "X", "XL", "L", "XC","C","CD","D","CM","M" };
        int[] charValuesArray = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
    
        String resultString = "";
        int temp = num;
        int [] resultValues = new int[13];
        // Generate an array which "char" occurances count
        for(int i = 12 ; i >= 0 ; i--)
        {
            if((temp / charValuesArray[i]) > 0)
            {
                resultValues[i] = temp/charValuesArray[i];
                temp = temp % charValuesArray[i];
            }
        }
        // Print them if not occured do not print
        for(int j = 12 ; j >= 0 ; j--)
        {
            for(int k = 0 ; k < resultValues[j]; k++)
            {
                resultString+= charsArray[j];
            }
        }
        return resultString;
    
    }
    
    0 讨论(0)
提交回复
热议问题