将整数转换为罗马数字

Kav*_*a L 54 c# c#-3.0 roman-numerals

我正在尝试编写一个将数字转换为罗马数字的函数.到目前为止这是我的代码; 但是,它仅适用于小于400的数字.是否有一种快速简便的方法来执行此转换,或扩展现有代码以便处理所有情况?在此先感谢您的帮助.

static string convertroman(int number)
    {
        int l = number / 10;
        StringBuilder sb = new StringBuilder();
        for (int m = 0; m <= l; m++)
        {
            if (l == 0)
            {
                break;
            }
            if (l == 5)
            {
                sb = sb.Append(ro.L.ToString());
                break;
            }
            if (l == 4)
            {
                sb = sb.Append(ro.X.ToString()).Append(ro.L.ToString());
                break;
            }
            if (l == 9)
            {
                sb = sb.Append(ro.X.ToString()).Append(ro.C.ToString());
                break;
            }
            if (l == 10)
            {
                sb = sb.Append(ro.C.ToString());
                break;
            }

            if (l > 5 && l < 9)
            {
                sb = sb.Append(ro.L.ToString());
                l = l - 5;
                m = 0;
                // break;
                continue;
            }
            if (l > 10)
            {
                sb = sb.Append(ro.C.ToString());
                l = l - 10;
                m = 0;
                // continue;

            }
            else
            {
                sb = sb.Append(ro.X.ToString());
            }

        }
        int z = number % 10;
        for (int x = 0; x <= z; x++)
        {
            if (z == 0)
            {
                break;
            }
            if (z == 5)
            {
                sb = sb.Append(ro.V.ToString());
                break;
            }
            if (z == 4)
            {
                sb = sb.Append(ro.I.ToString()).Append(ro.V.ToString());
                break;
            }
            if (z == 9)
            {
                sb = sb.Append(ro.I.ToString()).Append(ro.X.ToString());
                break;
            }
            if (z == 10)
            {
                sb = sb.Append(ro.X.ToString());
                break;
            }
            if (z > 5 && z < 9)
            {
                sb = sb.Append(ro.V.ToString());
                z = z - 5;
                x = 0;
            }
            else
            {
                sb.Append(ro.I.ToString());
            }              

        }
        return sb.ToString();           
    }
Run Code Online (Sandbox Code Playgroud)

Mos*_*ini 96

试试这个,简单而紧凑:

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");
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么递归不好?我认为这是一个非常简洁的解决方案 (9认同)
  • @CaptainCrypto你没错.解决方案绝对是聪明的,但我没有看到使用递归来解决使用"while"循环和字符串构建器可以轻松完成的事情.这不仅消除了堆栈问题,而且它还具有更高的性能和内存效率,因为您可以使用StringBuilder而不是连接字符串,并且您没有所有无关的方法调用. (5认同)
  • 只要有充分的理由,递归并不"坏".它确实有一些缺点.例如,请注意上面代码中的上限3999.如果你删除这个限制并在这个方法中抛出一个大数字,你可以很快得到一个堆栈溢出(没有双关语).而且,虽然我喜欢这种设计的简单性,但从内存的角度来看,由于字符串连接很多,因此效率不高. (4认同)
  • @MikeU此方法为它重复出现的每一层至少返回1个字符。充其量,它的递归深度将是返回的最终字符串的长度。令人怀疑的是,您是否会收到StackOverflowException,如果您这样做了,最终的罗马数字将太长而无法使用。(对于较大的数字,您只需要在给定字符上加一行以表示该字符的1000倍) (2认同)
  • 但是...这里的堆栈深度是“O(log(n))”。此外,罗马数字最多可以使用几位数字,也许是 8?与 StringBuilder 的区别是否可以衡量?出于这个原因,函数式编程应该被禁止,一切都应该在汇编程序中实现,因为只有这样你才能充分利用机器的能力。它可能会导致荒谬。 (2认同)

jby*_*yrd 21

这是一个更简单的算法 - 请原谅我,我不知道C#所以我用JavaScript写这个,但应该应用相同的算法(我已经评论过,所以你可以理解算法):

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
Run Code Online (Sandbox Code Playgroud)

  • [功能](https://gist.github.com/A1rPun/4ca5be3cce5f027ff42138c4dfa5b31e) 方法 `return int.toString().split("").reverse().reduce((acc, x, i) =&gt; ( i &lt; 3 ? romanNumerals[i][x] : "M".repeat(x)) + acc, "");`。工作范围为 1-9999。 (2认同)

Bru*_*oLM 19

我创建了这个类 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

一些单元测试To方法:

[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));
    }
}
Run Code Online (Sandbox Code Playgroud)

一些单元测试From方法:

[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"));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在NumberRomanDictionary中,没有90 - > XC (5认同)
  • 字典的顺序不是不确定的吗?如果是这样,如果“我”先出来会发生什么?那不是会一遍又一遍地打印 I 直到它达到零吗?另外,您不会检查是否有负数。 (3认同)

Jam*_*iec 12

这实际上是一个非常有趣的问题,并且基于dofactory.com上的反向示例(将罗马数字转换为小数),它很容易扭转模式,并且可能会稍微改进一下.此代码将支持1到3999999之间的数字.

从上下文类开始,这定义了解析器的I/O.

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一个抽象表达式,它定义了解析操作

public abstract class Expression
{
    public abstract void Interpret(Context value);
}
Run Code Online (Sandbox Code Playgroud)

现在,您需要一个抽象终端表达式,它定义将要执行的实际操作:

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();
}
Run Code Online (Sandbox Code Playgroud)

然后,定义罗马数字行为的类(注意,香港专业教育学院使用小写的惯例,其中罗马数字在字母上使用条形来表示1000次)

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; }
}
Run Code Online (Sandbox Code Playgroud)

几乎在那里,我们需要一个包含解析树的非终结表达式:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,客户端代码:

Context ctx = new Context(123);
var parser = new DecimalToRomaNumeralParser();
parser.Interpret(ctx);
Console.WriteLine(ctx.Output); // Outputs CXXIII
Run Code Online (Sandbox Code Playgroud)

实例:http://rextester.com/rundotnet?code = JJBYW89744


Ped*_*tos 9

在一行,不是很有效但工作:

public string RomanNumeralFrom(int number)
{
    return
        new string('I', number)
            .Replace(new string('I', 1000), "M")
            .Replace(new string('I', 900), "CM")
            .Replace(new string('I', 500), "D")
            .Replace(new string('I', 400), "CD")
            .Replace(new string('I', 100), "C")
            .Replace(new string('I', 90), "XC")
            .Replace(new string('I', 50), "L")
            .Replace(new string('I', 40), "XL")
            .Replace(new string('I', 10), "X")
            .Replace(new string('I', 9), "IX")
            .Replace(new string('I', 5), "V")
            .Replace(new string('I', 4), "IV");
}
Run Code Online (Sandbox Code Playgroud)


Mah*_*man 8

这应该是最简单的解决方案。

public string IntToRoman(int num)
{
    var result = string.Empty;
    var map = new Dictionary<string, int>
    {
        {"M", 1000 },
        {"CM", 900},
        {"D", 500},
        {"CD", 400},
        {"C", 100},
        {"XC", 90},
        {"L", 50},
        {"XL", 40},
        {"X", 10},
        {"IX", 9},
        {"V", 5},
        {"IV", 4},
        {"I", 1}
    };
    foreach (var pair in map)
    {
        result += string.Join(string.Empty, Enumerable.Repeat(pair.Key, num / pair.Value));
        num %= pair.Value;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)


xan*_*tos 5

这个版本不像其他版本那样“作弊”:它在内部生成“基本”表,其中包含所有“基本”“可组合”数字。对于懒惰,我使用Tuples,而不是创建专门的类。如果你没有C#4.0中,你可以替换Tuple<>使用KeyValuePair<>Item1KeyItem2Value

static Tuple<IList<Tuple<string, int>>, int> GenerateBaseNumbers()
{
    const string letters = "IVXLCDM";

    var tuples = new List<Tuple<string, int>>();
    Tuple<string, int> subtractor = null;

    int num = 1;
    int maxNumber = 0;

    for (int i = 0; i < letters.Length; i++)
    {
        string currentLetter = letters[i].ToString();

        if (subtractor != null)
        {
            tuples.Add(Tuple.Create(subtractor.Item1 + currentLetter, num - subtractor.Item2));
        }

        tuples.Add(Tuple.Create(currentLetter, num));

        bool isEven = i % 2 == 0;

        if (isEven)
        {
            subtractor = tuples[tuples.Count - 1];
        }

        maxNumber += isEven ? num * 3 : num;
        num *= isEven ? 5 : 2;
    }

    return Tuple.Create((IList<Tuple<string, int>>)new ReadOnlyCollection<Tuple<string, int>>(tuples), maxNumber);
}

static readonly Tuple<IList<Tuple<string, int>>, int> RomanBaseNumbers = GenerateBaseNumbers();

static string FromNumberToRoman(int num)
{
    if (num <= 0 || num > RomanBaseNumbers.Item2)
    {
        throw new ArgumentOutOfRangeException();
    }

    StringBuilder sb = new StringBuilder();

    int i = RomanBaseNumbers.Item1.Count - 1;

    while (i >= 0)
    {
        var current = RomanBaseNumbers.Item1[i];

        if (num >= current.Item2)
        {
            sb.Append(current.Item1);
            num -= current.Item2;
        }
        else
        {
            i--;
        }
    }

    return sb.ToString();
}

static void Main(string[] args)
{
    for (int i = 1; i <= RomanBaseNumbers.Item2; i++)
    {
        var calc = FromNumberToRoman(i);

        Console.WriteLine("{1}", i, calc);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*e U 5

虽然我喜欢 Mos\xc3\xa8 Bottacini\ 的答案,但在这种情况下使用递归会产生一些负面影响。一是可能的堆栈溢出,因此他限制了数字的上限。虽然,是的,我意识到罗马数字中的巨大数字看起来是多么荒谬,但这仍然是实现结果所不必要的限制。

\n\n

此外,由于字符串是不可变的,因此由于大量使用字符串连接,他的版本的内存效率将非常低。下面是我对其方法的修改版本,仅使用 while 循环和 StringBuilder。我的版本实际上应该具有更高的性能(尽管我们正在讨论亚毫秒范围内的差异)并且更容易占用系统内存。

\n\n
public static string ToRomanNumeral(this int value)\n{\n    if (value < 0)\n        throw new ArgumentOutOfRangeException("Please use a positive integer greater than zero.");\n\n    StringBuilder sb = new StringBuilder();\n    int remain = value;\n    while (remain > 0)\n    {\n        if (remain >= 1000) { sb.Append("M"); remain -= 1000; }\n        else if (remain >= 900) { sb.Append("CM"); remain -= 900; }\n        else if (remain >= 500) { sb.Append("D"); remain -= 500; }\n        else if (remain >= 400) { sb.Append("CD"); remain -= 400; }\n        else if (remain >= 100) { sb.Append("C"); remain -= 100; }\n        else if (remain >= 90) { sb.Append("XC"); remain -= 90; }\n        else if (remain >= 50) { sb.Append("L"); remain -= 50; }\n        else if (remain >= 40) { sb.Append("XL"); remain -= 40; }\n        else if (remain >= 10) { sb.Append("X"); remain -= 10; }\n        else if (remain >= 9) { sb.Append("IX"); remain -= 9; }\n        else if (remain >= 5) { sb.Append("V"); remain -= 5; }\n        else if (remain >= 4) { sb.Append("IV"); remain -= 4; }\n        else if (remain >= 1) { sb.Append("I"); remain -= 1; }\n        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).\n    }\n\n    return sb.ToString();\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 限制为 3999 的原因是因为在 3999 之后您使用顶部条形表示法。顶部栏 = 1,000 条,顶部和底部栏 = 1,000,000 条。 (3认同)

fub*_*ubo 5

这是DotNetSnippets 的一个超薄解决方案

private string ToRomanNumber(int number)
{
    StringBuilder result = new StringBuilder();
    int[] digitsValues = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
    string[] romanDigits = { "I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M" };
    while (number > 0)
    {
        for (int i = digitsValues.Count() - 1; i >= 0; i--)
            if (number / digitsValues[i] >= 1)
            {
                number -= digitsValues[i];
                result.Append(romanDigits[i]);
                break;
            }
    }
    return result.ToString();
}
Run Code Online (Sandbox Code Playgroud)