C#函数将数字转换为罗马数字

3

我正在尝试编写一个实用的函数,将罗马数字输入转换为十进制。我之前已经使用Javascript在两个方向上完成了这个功能,但是在C#版本中,我在循环中的while迭代中遇到了问题,还不知道如何让它正常工作。

class ToRoman
{
    public static int RomanToDecimal(string romanNums)
    {
        int result = 0; 

        int[] deci = new int[] {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string[] roman = new string[] {"M", "CM", "D", "CD", "C", "XD", "L", "XL", "X", "IX", "V", "IV", "I"};

        for (int i = 0; i < deci.Length; i++)
        {
            while (romanNums.IndexOf(roman[i] == romanNums[i])
            {
                result += deci[i];
                romanNums = romanNums.Replace(roman[i]," ");
            }
        }
        return result;
    }

    static void Main()
    {
        Console.WriteLine(RomanToDecimal("V"));
        Console.WriteLine(RomanToDecimal("XIX"));
        Console.WriteLine(RomanToDecimal("MDXXVI"));
        Console.WriteLine(RomanToDecimal("MCCCXXXVII"));
    }
}

你有什么问题? - Tim Schmelter
你应该看一下这个:https://dev59.com/VWw05IYBdhLWcg3wxkmr#11749642 - Ricardo Pontual
2
很确定这段代码无法编译。这一行:while (romanNums.IndexOf(roman[i] == romanNums[i]) 是无效的语法。 - stelioslogothetis
只是一条注释 - 90应该是XC,而不是XD。 - PaulF
你是想写 while (romanNums.IndexOf(roman[i]) >= 0) 还是 while (romanNums.IndexOf(roman[i]) == i) - Olivier Jacot-Descombes
显示剩余2条评论
1个回答

2
你的方法存在一个非常严重的问题,就是必须使用贪心算法:例如,如果你输入了CDL,应该将其视为CD + L == 450,而不是C + D + L == 650。另一个(可能较小的)问题是,你允许输入不正确的内容,例如DDIIVXM等。
可以通过使用有限状态自动机(FSA)编写一个带有语法检查的准确实现。例如,以下输入在语法上是不正确的:
MIM, LL, XLX, CDC, XXXX, CCCXCX, CCCXL, VL, VX

类似这样:

private static Dictionary<char, int> s_Romans = new Dictionary<char, int>() {
  {'M', 1000}, {'D', 500}, {'C', 100}, {'L', 50}, {'X', 10}, {'V', 5}, {'I', 1},
};

private static int RomanToArabic(string value) {
  if (null == value)
    throw new ArgumentNullException("value");
  else if (string.IsNullOrWhiteSpace(value))
    throw new ArgumentException("Empty or WS only value is not allowed.", "value");

  int v;

  int[] values = value
    .Select(c => s_Romans.TryGetValue(c, out v) ? v : -1)
    .ToArray();

  int result = 0;
  int current = 1000;
  int count = 0;

  for (int i = 0; i < values.Length; ++i) {
    v = values[i];

    if (v < 0)
      throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
    else if (v > current)
      throw new FormatException($"Invalid symbol {value[i]}");
    else if (current == v) {
      count += 1;

      if (count > 1 && (v == 5 || v == 50 || v == 500))
        throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
      else if (count > 3 && current != 1000)
        throw new FormatException($"Invalid symbol {value[i]} at {i + 1}");
    }
    else {
      count = 1;
      current = v;
    }

    if (i < value.Length - 1)
      if (v == 1 || v == 10 || v == 100)
        if (v * 5 == values[i + 1] || v * 10 == values[i + 1]) {
          v = values[i + 1] - v;
          count = 3;
          
          i += 1;
        }

    result += v;
  }

  return result;
}

一些测试:

// 4000
Console.Write(RomanToArabic("MMMM")); 
// 1444
Console.Write(RomanToArabic("MCDXLIV"));
// 1009
Console.Write(RomanToArabic("MIX"));
// 1
Console.Write(RomanToArabic("I"));

更多测试:

将整数转换为罗马数字

// Mosè Bottacini's code see the link above
public static string ToRoman(int number) {
  if ((number < 0) || (number > 3999)) 
    throw new ArgumentOutOfRangeException("insert value between 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");
}

...

var failed = Enumerable.Range(1, 3000)
    .Select(i => new {
      arabic = i,
      roman = ToRoman(i)
    })
    .Where(item => item.arabic != RomanToArabic(item.roman))
    .Select(item => $"{item.roman} expected: {item.arabic} actual: {RomanToArabic(item.roman)}");

// No line should be printed out
Console.Write(string.Join(Environment.NewLine, failed)); 

CD + L 不是 450 吗?其中 CD = 500 -100L = 50 - Olivier Jacot-Descombes
1
@Olivier Jacot-Descombes:谢谢!你提到的CDL == 450肯定没错。 - Dmitry Bychenko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接