Converting integers to roman numerals

前端 未结 29 2298
走了就别回头了
走了就别回头了 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:50

    One more straight forward solution. Trying to improve slightly the performance I use StringBuilder, iterate through less keys (one the other site of course LINQ-where might add additional delay)

    public class ArabicToRomanConverter
    {
        private static readonly Dictionary<int, string> _romanDictionary = 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 ArabicToRomanConverter()
        {
    
        }
    
        public string Convert(int arabicNumber)
        {
            StringBuilder romanNumber = new StringBuilder();
            var keys = _romanDictionary.Keys.Where(k => arabicNumber >= k).ToList();
            for (int i = 0; i < keys.Count && arabicNumber > 0; i++)
            {
                int ckey = keys[i];
                int division = arabicNumber / ckey;
                if (division != 0)
                {
                    for (int j = 0; j < division; j++)
                    {
                        romanNumber.Append(_romanDictionary[ckey]);
                        arabicNumber -= ckey;
                    }
                }
            }
    
            return romanNumber.ToString();
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:50
    First create list of Tuples which contains numbers and corresponds.
    Then a method/loops to iterate and return result.
    
    IEnumerable<Tuple<int, string>> data = new List<Tuple<int, string>>()
                    {
                      new Tuple<int, string>( 1, "I"),
                      new Tuple<int, string>( 4, "IV" ),
                      new Tuple<int, string>( 5, "V" ),
                      new Tuple<int, string>( 9, "IX" ),
                      new Tuple<int, string>( 10, "X" ),
                      new Tuple<int, string>( 40, "XL" ),
                      new Tuple<int, string>( 50, "L" ),
                      new Tuple<int, string>( 90, "XC" ),
                      new Tuple<int, string>( 100, "C" ),
                      new Tuple<int, string>( 400, "CD" ),
                      new Tuple<int, string>( 500, "D" ),
                      new Tuple<int, string>( 900, "CM"),
                      new Tuple<int, string>( 1000, "M" )
                    };
    
    
     public string ToConvert(decimal num)
                { 
                     data = data.OrderByDescending(o => o.Item1).ToList(); 
                    List<Tuple<int, string>> subData = data.Where(w => w.Item1 <= num).ToList();
                    StringBuilder sb = new StringBuilder();
                    foreach (var item in subData)
                    {
                        if (num >= item.Item1)
                        {
                            while (num >= item.Item1)
                            {
                                num -= item.Item1;
                                sb.Append(item.Item2.ToUpper());
                            }
                        }
                    } 
                    return sb.ToString();
                }
    
    0 讨论(0)
  • 2020-12-02 09:51
            Random r = new Random();
            int[] arreglo = new int[100];
            for (int i=0; i<arreglo.Length;i++) {
                arreglo[i] = r.Next(1,1001);
            }
    
            for (int t = 0;t < arreglo.Length; t++)
            {
                if (arreglo[t] >= 1000)
                {
                    Console.Write("M"); arreglo[t] -= 1000;
                }
                if (arreglo[t] >=900)
                {
                    Console.Write("MC"); arreglo[t] -= 900;
                }
                if (arreglo[t] >= 500)
                {
                    Console.Write("D"); arreglo[t] -= 500;
                }
                if (arreglo[t] >= 400)
                {
                    Console.Write("CD"); arreglo[t] -= 400;
                }
                if (arreglo[t] >= 100) {
                    Console.Write("C"); arreglo[t] -= 100;
                }
                if (arreglo[t] >= 90)
                {
                    Console.Write("XC"); arreglo[t] -= 90;
                }
                if (arreglo[t] >= 50)
                {
                    Console.Write("L"); arreglo[t] -= 50;
                }
                if (arreglo[t] >= 40)
                {
                    Console.Write("XL"); arreglo[t] -= 40;
                }
                if (arreglo[t] >= 10)
                {
                    Console.Write("X"); arreglo[t] -= 10;
                }
                if (arreglo[t] >= 9)
                {
                    Console.Write("IX"); arreglo[t] -= 9;
                }
                if (arreglo[t] >= 5)
                {
                    Console.Write("V"); arreglo[t] -= 5;
                }
                if (arreglo[t] >= 4)
                {
                    Console.Write("IV"); arreglo[t] -= 4;
                }
                if (arreglo[t] >= 1)
                {
                    Console.Write("I"); arreglo[t] -= 1;
                }
    
                Console.WriteLine();
    
            }
            Console.ReadKey();    
    
    0 讨论(0)
  • 2020-12-02 09:51

    Thought this problem was interesting, this was my take on it.

    It should (hopefully) deal with numbers up to the upper limit of overscored characters. Adding any other conventions should be just a matter of configuring new bands and adjusting the ConfigureNext chain.

    NumeralGenerator.cs

    public static class NumeralGenerator
    {
        private static readonly INumeralBand RootNumeralBand = ConfigureMapping();
    
        private static INumeralBand ConfigureMapping()
        {
            var unitBand = new FinalBand(1, "I");
    
            var fiveBand = new NumeralBand(5, "V", unitBand);
            var tenBand = new NumeralBand(10, "X", unitBand);
            var fiftyBand = new NumeralBand(50, "L", tenBand);
            var hundredBand = new NumeralBand(100, "C", tenBand);
            var fiveHundredBand = new NumeralBand(500, "D", hundredBand);
            var thousandBand = new NumeralBand(1000, "M", hundredBand);
    
            var thousandUnitBand = new NumeralBand(1000, "I\u0305", thousandBand);
    
            var fiveThousandBand = new NumeralBand(5000, "V\u0305", thousandUnitBand);
            var tenThousandBand = new NumeralBand(10000, "X\u0305", thousandUnitBand);
            var fiftyThousandBand = new NumeralBand(50000, "L\u0305", tenThousandBand);
            var hundredThousandBand = new NumeralBand(100000, "C\u0305", tenThousandBand);
            var fiveHundredThousandBand = new NumeralBand(500000, "D\u0305", hundredThousandBand);
            var millionBand = new NumeralBand(1000000, "M\u0305", hundredThousandBand);
    
            millionBand
                .ConfigureNext(fiveHundredThousandBand)
                .ConfigureNext(hundredThousandBand)
                .ConfigureNext(fiftyThousandBand)
                .ConfigureNext(tenThousandBand)
                .ConfigureNext(fiveThousandBand)
                .ConfigureNext(thousandBand)
                .ConfigureNext(fiveHundredBand)
                .ConfigureNext(hundredBand)
                .ConfigureNext(fiftyBand)
                .ConfigureNext(tenBand)
                .ConfigureNext(fiveBand)
                .ConfigureNext(unitBand);
    
            return millionBand;
        }
    
        public static string ToNumeral(int number)
        {
            var numerals = new StringBuilder();
    
            RootNumeralBand.Process(number, numerals);
    
            return numerals.ToString();
        }
    }
    

    INumeralBand.cs

    public interface INumeralBand
    {
        int Value { get; }
        string Numeral { get; }
    
        void Process(int number, StringBuilder numerals);
    }
    

    NumeralBand.cs

    public class NumeralBand : INumeralBand
    {
        private readonly INumeralBand _negatedBy;
        private INumeralBand _nextBand;
    
        public NumeralBand(int value, string numeral, INumeralBand negatedBy)
        {
            _negatedBy = negatedBy;
    
            Value = value;
            Numeral = numeral;
        }
    
        public int Value { get; }
        public string Numeral { get; }
    
        public void Process(int number, StringBuilder numerals)
        {
            if (ShouldNegateAndStop(number))
            {
                numerals.Append(NegatedNumeral);
                return;
            }
    
            var numeralCount = Math.Abs(number / Value);
            var remainder = number % Value;
    
            numerals.Append(string.Concat(Enumerable.Range(1, numeralCount).Select(x => Numeral)));
    
            if (ShouldNegateAndContinue(remainder))
            {
                NegateAndContinue(numerals, remainder);
                return;
            }
    
            if (remainder > 0)
                _nextBand.Process(remainder, numerals);
        }
    
        private string NegatedNumeral => $"{_negatedBy.Numeral}{Numeral}";
    
        private bool ShouldNegateAndStop(int number) => number == Value - _negatedBy.Value;
        private bool ShouldNegateAndContinue(int number) => number >= Value - _negatedBy.Value;
    
        private void NegateAndContinue(StringBuilder stringBuilder, int remainder)
        {
            stringBuilder.Append(NegatedNumeral);
            remainder = remainder % (Value - _negatedBy.Value);
    
            _nextBand.Process(remainder, stringBuilder);
        }
    
        public T ConfigureNext<T>(T nextBand) where T : INumeralBand
        {
            _nextBand = nextBand;
    
            return nextBand;
        }
    }
    

    FinalBand.cs

    public class FinalBand : INumeralBand
    {
        public FinalBand(int value, string numeral)
        {
            Value = value;
            Numeral = numeral;
        }
    
        public int Value { get; }
        public string Numeral { get; }
    
        public void Process(int number, StringBuilder numerals)
        {
            numerals.Append(new string(Numeral[0], number));
        }
    }
    

    The Tests:

    FinalBandTests.cs

    public class FinalBandTests
    {
        [Theory]
        [InlineData(1, "I")]
        [InlineData(2, "II")]
        [InlineData(3, "III")]
        [InlineData(4, "IIII")]
        public void Process(int number, string expected)
        {
            var stringBuilder = new StringBuilder();
    
            var numeralBand = new FinalBand(1, "I");
            numeralBand.Process(number, stringBuilder);
    
            Assert.Equal(expected, stringBuilder.ToString());
        }
    }
    

    NumeralBandTests.cs

    public class NumeralBandTests
    {
        private Mock<INumeralBand> _nextBand;
        private Mock<INumeralBand> _negatedBy;
    
        private StringBuilder _stringBuilder;
    
        public NumeralBandTests()
        {
            _stringBuilder = new StringBuilder();
    
            _nextBand = new Mock<INumeralBand>();
            _negatedBy = new Mock<INumeralBand>();
        }
    
        [Fact]
        public void Process_NegateAndStop()
        {
            var numeral = new NumeralBand(10, "X", _negatedBy.Object);
    
            _negatedBy.Setup(x => x.Value).Returns(1);
            _negatedBy.Setup(x => x.Numeral).Returns("I");
    
            numeral.Process(9, _stringBuilder);
    
            Assert.Equal("IX", _stringBuilder.ToString());
            _nextBand.Verify(x => x.Process(It.IsAny<int>(), It.IsAny<StringBuilder>()), Times.Never);
        }
    
        [Fact]
        public void Process_Exact()
        {
            var numeral = new NumeralBand(10, "X", _negatedBy.Object);
    
            _negatedBy.Setup(x => x.Value).Returns(1);
            _negatedBy.Setup(x => x.Numeral).Returns("I");
    
            numeral.Process(10, _stringBuilder);
    
            Assert.Equal("X", _stringBuilder.ToString());
            _nextBand.Verify(x => x.Process(It.IsAny<int>(), It.IsAny<StringBuilder>()), Times.Never);
        }
    
        [Fact]
        public void Process_NegateAndContinue()
        {
            var numeral = new NumeralBand(50, "L", _negatedBy.Object);
            numeral.ConfigureNext(_nextBand.Object);
    
            _negatedBy.Setup(x => x.Value).Returns(10);
            _negatedBy.Setup(x => x.Numeral).Returns("X");
    
            numeral.Process(54, _stringBuilder);
    
            Assert.Equal("L", _stringBuilder.ToString());
            _nextBand.Verify(x => x.Process(4, _stringBuilder), Times.Once);
        }
    }
    

    NumeralGeneratorTests.cs

    public class NumeralGeneratorTests
    {
        private readonly ITestOutputHelper _output;
    
        public NumeralGeneratorTests(ITestOutputHelper output)
        {
            _output = output;
        }
    
        [Theory]
        [InlineData(1, "I")]
        [InlineData(2, "II")]
        [InlineData(3, "III")]
        [InlineData(4, "IV")]
        [InlineData(5, "V")]
        [InlineData(6, "VI")]
        [InlineData(7, "VII")]
        [InlineData(8, "VIII")]
        [InlineData(9, "IX")]
        [InlineData(10, "X")]
        [InlineData(11, "XI")]
        [InlineData(15, "XV")]
        [InlineData(1490, "MCDXC")]
        [InlineData(1480, "MCDLXXX")]
        [InlineData(1580, "MDLXXX")]
        [InlineData(1590, "MDXC")]
        [InlineData(1594, "MDXCIV")]
        [InlineData(1294, "MCCXCIV")]
        [InlineData(3999, "MMMCMXCIX")]
        [InlineData(4000, "I\u0305V\u0305")]
        [InlineData(4001, "I\u0305V\u0305I")]
        [InlineData(5002, "V\u0305II")]
        [InlineData(10000, "X\u0305")]
        [InlineData(15000, "X\u0305V\u0305")]
        [InlineData(15494, "X\u0305V\u0305CDXCIV")]
        [InlineData(2468523, "M\u0305M\u0305C\u0305D\u0305L\u0305X\u0305V\u0305MMMDXXIII")]
        public void ToNumeral(int number, string expected)
        {
            var sw = Stopwatch.StartNew();
            var actual = NumeralGenerator.ToNumeral(number);
            sw.Stop();
    
            _output.WriteLine(sw.ElapsedMilliseconds.ToString());
    
            Assert.Equal(expected, actual);
        }
    }
    
    0 讨论(0)
  • 2020-12-02 09:53

    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

    0 讨论(0)
  • 2020-12-02 09:55

    Here's my effort, built with extension in mind and hopefully easy to understand, probably not the fastest method though. I wanted to complete this as got as part of interview test(which was very demoralising), requires an understanding of the problem first though in order to tackle it.

    It should do all numbers, can be checked on here https://www.calculateme.com/roman-numerals/from-roman

            static void Main(string[] args)
        {
    
            CalculateRomanNumerals(1674);
        }
    
        private static void CalculateRomanNumerals(int integerInput)
        {
            foreach (var item in Enum.GetValues(typeof(RomanNumerals)).Cast<int>().Reverse())
            {
                integerInput = ProcessNumber(integerInput, item);
            }
    
            Console.ReadKey();
        }
    
        private static int ProcessNumber(int input, int number)
        {
            while (input >= number)
            {
                input -= number;
                Console.Write((RomanNumerals)number);
            }
    
            return input;
        }
    
        enum RomanNumerals : int
        {
            I = 1,
            IV = 4,
            V = 5,
            IX = 9,
            X = 10,
            L = 50,
            XC = 90,
            C = 100,
            CD = 400,
            D = 500,
            CM = 900,
            M = 1000
        }
    
    0 讨论(0)
提交回复
热议问题