在数据中,有时相同的产品会用罗马数字命名,而其他时候会用数字命名。
示例 Samsung Galaxy SII
经文Samsung Galaxy S2
如何将II
其转换为价值2
?
在数据中,有时相同的产品会用罗马数字命名,而其他时候会用数字命名。
示例 Samsung Galaxy SII
经文Samsung Galaxy S2
如何将II
其转换为价值2
?
我注意到这里有一些非常复杂的解决方案,但这是一个非常简单的问题。我提出了一个解决方案,避免了对“异常”(IV、IX、XL 等)进行硬编码的需要。我使用for
循环来查看罗马数字字符串中的下一个字符,以查看与数字相关的数字是否应该减去或添加到总数中。为简单起见,我假设所有输入都是有效的。
private static Dictionary<char, int> RomanMap = new Dictionary<char, int>()
{
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
public static int RomanToInteger(string roman)
{
int number = 0;
for (int i = 0; i < roman.Length; i++)
{
if (i + 1 < roman.Length && RomanMap[roman[i]] < RomanMap[roman[i + 1]])
{
number -= RomanMap[roman[i]];
}
else
{
number += RomanMap[roman[i]];
}
}
return number;
}
我最初尝试foreach
在字符串上使用 a,我认为这是一个更易读的解决方案,但我最终将每个数字相加,然后如果结果是我不喜欢的例外之一,则将其减去两次。无论如何,我都会在这里发布以供后代使用。
public static int RomanToInteger(string roman)
{
int number = 0;
char previousChar = roman[0];
foreach(char currentChar in roman)
{
number += RomanMap[currentChar];
if(RomanMap[previousChar] < RomanMap[currentChar])
{
number -= RomanMap[previousChar] * 2;
}
previousChar = currentChar;
}
return number;
}
这是我的解决方案
public int SimplerConverter(string number)
{
number = number.ToUpper();
var result = 0;
foreach (var letter in number)
{
result += ConvertLetterToNumber(letter);
}
if (number.Contains("IV")|| number.Contains("IX"))
result -= 2;
if (number.Contains("XL")|| number.Contains("XC"))
result -= 20;
if (number.Contains("CD")|| number.Contains("CM"))
result -= 200;
return result;
}
private int ConvertLetterToNumber(char letter)
{
switch (letter)
{
case 'M':
{
return 1000;
}
case 'D':
{
return 500;
}
case 'C':
{
return 100;
}
case 'L':
{
return 50;
}
case 'X':
{
return 10;
}
case 'V':
{
return 5;
}
case 'I':
{
return 1;
}
default:
{
throw new ArgumentException("Ivalid charakter");
}
}
}
一个更简单易读的 C# 实现:
代码:
private static Dictionary<char, int> _romanMap = new Dictionary<char, int>
{
{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};
public static int ConvertRomanToNumber(string text)
{
int totalValue = 0, prevValue = 0;
foreach (var c in text)
{
if (!_romanMap.ContainsKey(c))
return 0;
var crtValue = _romanMap[c];
totalValue += crtValue;
if (prevValue != 0 && prevValue < crtValue)
{
if (prevValue == 1 && (crtValue == 5 || crtValue == 10)
|| prevValue == 10 && (crtValue == 50 || crtValue == 100)
|| prevValue == 100 && (crtValue == 500 || crtValue == 1000))
totalValue -= 2 * prevValue;
else
return 0;
}
prevValue = crtValue;
}
return totalValue;
}
我刚才写了一个简单的罗马数字转换器,但它并没有做很多错误检查,但它似乎适用于我可以扔给它的所有格式正确的东西。
public class RomanNumber
{
public string Numeral { get; set; }
public int Value { get; set; }
public int Hierarchy { get; set; }
}
public List<RomanNumber> RomanNumbers = new List<RomanNumber>
{
new RomanNumber {Numeral = "M", Value = 1000, Hierarchy = 4},
//{"CM", 900},
new RomanNumber {Numeral = "D", Value = 500, Hierarchy = 4},
//{"CD", 400},
new RomanNumber {Numeral = "C", Value = 100, Hierarchy = 3},
//{"XC", 90},
new RomanNumber {Numeral = "L", Value = 50, Hierarchy = 3},
//{"XL", 40},
new RomanNumber {Numeral = "X", Value = 10, Hierarchy = 2},
//{"IX", 9},
new RomanNumber {Numeral = "V", Value = 5, Hierarchy = 2},
//{"IV", 4},
new RomanNumber {Numeral = "I", Value = 1, Hierarchy = 1}
};
/// <summary>
/// Converts the roman numeral to int, assumption roman numeral is properly formatted.
/// </summary>
/// <param name="romanNumeralString">The roman numeral string.</param>
/// <returns></returns>
private int ConvertRomanNumeralToInt(string romanNumeralString)
{
if (romanNumeralString == null) return int.MinValue;
var total = 0;
for (var i = 0; i < romanNumeralString.Length; i++)
{
// get current value
var current = romanNumeralString[i].ToString();
var curRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == current.ToUpper());
// last number just add the value and exit
if (i + 1 == romanNumeralString.Length)
{
total += curRomanNum.Value;
break;
}
// check for exceptions IV, IX, XL, XC etc
var next = romanNumeralString[i + 1].ToString();
var nextRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == next.ToUpper());
// exception found
if (curRomanNum.Hierarchy == (nextRomanNum.Hierarchy - 1))
{
total += nextRomanNum.Value - curRomanNum.Value;
i++;
}
else
{
total += curRomanNum.Value;
}
}
return total;
}
我来到这里寻找罗马数字解析器的小型实现,但在大小和优雅方面对所提供的答案并不满意。我将最终的递归实现留在这里,以帮助其他人搜索小型实现。
XIIX
)。/[mdclxvi]*/i
)罗马数字。// returns the value for a roman literal
private static int romanValue(int index)
{
int basefactor = ((index % 2) * 4 + 1); // either 1 or 5...
// ...multiplied with the exponentation of 10, if the literal is `x` or higher
return index > 1 ? (int) (basefactor * System.Math.Pow(10.0, index / 2)) : basefactor;
}
public static int FromRoman(string roman)
{
roman = roman.ToLower();
string literals = "mdclxvi";
int value = 0, index = 0;
foreach (char literal in literals)
{
value = romanValue(literals.Length - literals.IndexOf(literal) - 1);
index = roman.IndexOf(literal);
if (index > -1)
return FromRoman(roman.Substring(index + 1)) + (index > 0 ? value - FromRoman(roman.Substring(0, index)) : value);
}
return 0;
}
尝试使用这个 .Netfiddle:https ://dotnetfiddle.net/veaNk3
该算法通过从罗马数字中获取最高值并递归地加/减文字剩余左/右部分的值来计算罗马数字的值。
ii X iiv # Pick the greatest value in the literal `iixiiv` (symbolized by uppercase)
然后递归地重新计算并减去左侧并添加右侧:
(iiv) + x - (ii) # Subtract the lefthand-side, add the righthand-side
(V - (ii)) + x - ((I) + i) # Pick the greatest values, again
(v - ((I) + i)) + x - ((i) + i) # Pick the greatest value of the last numeral compound
最后将数字替换为它们的整数值:
(5 - ((1) + 1)) + 10 - ((1) + 1)
(5 - (2)) + 10 - (2)
3 + 10 - 2
= 11
使用Humanizer库,您可以使用FromRoman扩展将罗马数字更改为数字
"IV".FromRoman() => 4
并使用ToRoman扩展进行反向操作。
4.ToRoman() => "IV"
完成“减法”语义检查的解决方案
当前的解决方案都没有完全满足“减法”的整套规则。“IIII”-> 是不可能的。每个解决方案的结果都是 4。字符串:“CCCC”、“VV”、“IC”、“IM”也是无效的。
一个很好的检查语义的在线转换器是https://www.romannumerals.org/converter 所以,如果你真的想做一个完整的语义检查,它要复杂得多。
我的方法是首先编写带有语义检查的单元测试。然后写代码。然后用一些 linq 表达式减少循环。
也许有一个更聪明的解决方案,但我认为以下代码满足了转换罗马数字字符串的规则。
在代码部分之后,有一个部分包含我的单元测试。
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;
}
}
这是单元测试
[TestFixture]
public class RomanNumeralsTests
{
[Test]
public void TranslateRomanNumeral_WhenArgumentIsNull_RaiseArgumentNullException()
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(null));
}
[TestCase("A")]
[TestCase("-")]
[TestCase("BXA")]
[TestCase("MMXK")]
public void TranslateRomanNumeral_WhenInvalidNumeralSyntax_RaiseException(string input)
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
}
[TestCase("IIII")]
[TestCase("CCCC")]
[TestCase("VV")]
[TestCase("IC")]
[TestCase("IM")]
[TestCase("XM")]
[TestCase("IL")]
[TestCase("MCDXCXI")]
[TestCase("MCDDXC")]
public void TranslateRomanNumeral_WhenInvalidNumeralSemantics_RaiseException(string input)
{
var romanNumerals = new RomanNumerals();
Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
}
[TestCase("I", 1)]
[TestCase("II", 2)]
[TestCase("III", 3)]
[TestCase("IV", 4)]
[TestCase("XLII", 42)]
[TestCase("MMXIII", 2013)]
[TestCase("MXI", 1011)]
[TestCase("MCDXCIX", 1499)]
[TestCase("MMXXII", 2022)]
[TestCase("V", 5)]
[TestCase("VI", 6)]
[TestCase("CX", 110)]
[TestCase("CCCLXXV", 375)]
[TestCase("MD", 1500)]
[TestCase("MDLXXV", 1575)]
[TestCase("MDCL", 1650)]
[TestCase("MDCCXXV", 1725)]
[TestCase("MDCCC", 1800)]
[TestCase("MDCCCLXXV", 1875)]
[TestCase("MCML", 1950)]
[TestCase("MMXXV", 2025)]
[TestCase("MMC", 2100)]
[TestCase("MMCLXXV", 2175)]
[TestCase("MMCCL", 2250)]
[TestCase("MMCCCXXV", 2325)]
[TestCase("MMCD", 2400)]
[TestCase("MMCDLXXV", 2475)]
[TestCase("MMDL", 2550)]
[TestCase("MMMMMMMM", 8000)]
[TestCase("MMMMMMMMIV", 8004)]
public void TranslateRomanNumeral_WhenValidNumeral_Translate(string input, int output)
{
var romanNumerals = new RomanNumerals();
var result = romanNumerals.TranslateRomanNumeral(input);
Assert.That(result.Equals(output));
}
}
private static int convertRomanToInt(String romanNumeral)
{
Dictionary<Char, Int32> romanMap = new Dictionary<char, int>
{
{'I', 1 },
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
Int32 result = 0;
for (Int32 index = romanNumeral.Length - 1, last = 0; index >= 0; index--)
{
Int32 current = romanMap[romanNumeral[index]];
result += (current < last ? -current : current);
last = current;
}
return result;
}
System.Linq
从这个借了很多。String
implements IEnumerable<char>
,所以我认为这是合适的,因为无论如何我们都将它视为可枚举的对象。针对一堆随机数进行了测试,包括 1、3、4、8、83、99、404、555、846、927、1999、2420。
public static IDictionary<char, int> CharValues
{
get
{
return new Dictionary<char, int>
{{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}};
}
}
public static int RomanNumeralToInteger(IEnumerable<char> romanNumerals)
{
int retVal = 0;
//go backwards
for (int i = romanNumerals.Count() - 1; i >= 0; i--)
{
//get current character
char c = romanNumerals.ElementAt(i);
//error checking
if (!CharValues.ContainsKey(c)) throw new InvalidRomanNumeralCharacterException(c);
//determine if we are adding or subtracting
bool op = romanNumerals.Skip(i).Any(rn => CharValues[rn] > CharValues[c]);
//then do so
retVal = op ? retVal - CharValues[c] : retVal + CharValues[c];
}
return retVal;
}
private static Dictionary<char, int> RomanNumberMap = new Dictionary<char, int> {
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};
private const string RomanNumberValidationRegEx = "^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$";
private static int ConvertToRomanNumberToInteger(string romanNumber)
{
if (!Regex.IsMatch(romanNumber, RomanNumberValidationRegEx))
{
throw new ArgumentOutOfRangeException(romanNumber);
}
int result = 0;
for (int i = 0; i < romanNumber.Length; i++)
{
int currentVal = RomanNumberMap[romanNumber[i]];
if (romanNumber.Length > i + 1)
{
int nextVal = RomanNumberMap[romanNumber[i + 1]];
if (nextVal > currentVal)
{
result = result + (nextVal - currentVal);
i++;
continue;
}
}
result = result + currentVal;
}
return result;
}
我将通过在 .net 中使用数组来建议一种最简单的方法:C# 部分中给出了注释以进行解释
VB.net
Public Class Form1
Dim indx() As Integer = {1, 2, 3, 4, 5, 10, 50, 100, 500, 1000}
Dim row() As String = {"I", "II", "III", "IV", "V", "X", "L", "C", "D", "M"}
Dim limit As Integer = 9
Dim output As String = ""
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim num As Integer
output = ""
num = CInt(txt1.Text)
While num > 0
num = find(num)
End While
txt2.Text = output
End Sub
Public Function find(ByVal Num As Integer) As Integer
Dim i As Integer = 0
While indx(i) <= Num
i += 1
End While
If i <> 0 Then
limit = i - 1
Else
limit = 0
End If
output = output & row(limit)
Num = Num - indx(limit)
Return Num
End Function
End Class
C#
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class Form1
{
int[] indx = {
1,
2,
3,
4,
5,
10,
50,
100,
500,
1000
// initialize array of integers
};
string[] row = {
"I",
"II",
"III",
"IV",
"V",
"X",
"L",
"C",
"D",
"M"
//Carasponding roman letters in for the numbers in the array
};
// integer to indicate the position index for link two arrays
int limit = 9;
//string to store output
string output = "";
private void Button1_Click(System.Object sender, System.EventArgs e)
{
int num = 0;
// stores the input
output = "";
// clear output before processing
num = Convert.ToInt32(txt1.Text);
// get integer value from the textbox
//Loop until the value became 0
while (num > 0) {
num = find(num);
//call function for processing
}
txt2.Text = output;
// display the output in text2
}
public int find(int Num)
{
int i = 0;
// loop variable initialized with 0
//Loop until the indx(i).value greater than or equal to num
while (indx(i) <= Num) {
i += 1;
}
// detemine the value of limit depends on the itetration
if (i != 0) {
limit = i - 1;
} else {
limit = 0;
}
output = output + row(limit);
//row(limit) is appended with the output
Num = Num - indx(limit);
// calculate next num value
return Num;
//return num value for next itetration
}
}
这是我在 JavaScript 中的 O(n) 解决方案
const NUMERAL = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}
var romanToInt = function(s) {
if (s.length === 1) return +NUMERAL[s];
let number = 0;
for (let i = 0; i < s.length; i++) {
let num = +NUMERAL[s[i]];
let prev = +NUMERAL[s[i-1]];
if (prev < num) number += num - (2 * prev);
else number += num;
}
return number;
};
我只使用数组编写了这个。
我在这里省略了测试代码,但它看起来可以正常工作。
public static class RomanNumber {
static string[] units = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
static string[] tens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
static string[] hundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
static string[] thousands = { "", "M", "MM", "MMM" };
static public bool IsRomanNumber(string source) {
try {
return RomanNumberToInt(source) > 0;
}
catch {
return false;
}
}
/// <summary>
/// Parses a string containing a roman number.
/// </summary>
/// <param name="source">source string</param>
/// <returns>The integer value of the parsed roman numeral</returns>
/// <remarks>
/// Throws an exception on invalid source.
/// Throws an exception if source is not a valid roman number.
/// Supports roman numbers from "I" to "MMMCMXCIX" ( 1 to 3999 )
/// NOTE : "IMMM" is not valid</remarks>
public static int RomanNumberToInt(string source) {
if (String.IsNullOrWhiteSpace(source)) {
throw new ArgumentNullException();
}
int total = 0;
string buffer = source;
// parse the last four characters in the string
// each time we check the buffer against a number array,
// starting from units up to thousands
// we quit as soon as there are no remaing characters to parse
total += RipOff(buffer, units, out buffer);
if (buffer != null) {
total += (RipOff(buffer, tens, out buffer)) * 10;
}
if (buffer != null) {
total += (RipOff(buffer, hundreds, out buffer)) * 100;
}
if (buffer != null) {
total += (RipOff(buffer, thousands, out buffer)) * 1000;
}
// after parsing for thousands, if there is any character left, this is not a valid roman number
if (buffer != null) {
throw new ArgumentException(String.Format("{0} is not a valid roman number", buffer));
}
return total;
}
/// <summary>
/// Given a string, takes the four characters on the right,
/// search an element in the numbers array and returns the remaing characters.
/// </summary>
/// <param name="source">source string to parse</param>
/// <param name="numbers">array of roman numerals</param>
/// <param name="left">remaining characters on the left</param>
/// <returns>If it finds a roman numeral returns its integer value; otherwise returns zero</returns>
public static int RipOff(string source, string[] numbers, out string left) {
int result = 0;
string buffer = null;
// we take the last four characters : this is the length of the longest numeral in our arrays
// ("VIII", "LXXX", "DCCC")
// or all if source length is 4 or less
if (source.Length > 4) {
buffer = source.Substring(source.Length - 4);
left = source.Substring(0, source.Length - 4);
}
else {
buffer = source;
left = null;
}
// see if buffer exists in the numbers array
// if it does not, skip the first character and try again
// until buffer contains only one character
// append the skipped character to the left arguments
while (!numbers.Contains(buffer)) {
if (buffer.Length == 1) {
left = source; // failed
break;
}
else {
left += buffer.Substring(0, 1);
buffer = buffer.Substring(1);
}
}
if (buffer.Length > 0) {
if (numbers.Contains(buffer)) {
result = Array.IndexOf(numbers, buffer);
}
}
return result;
}
}
}
编辑
忘记它!
只需在此处查看BrunoLM解决方案。
它简单而优雅。
唯一需要注意的是它不检查来源。
这个使用堆栈:
public int RomanToInt(string s)
{
var dict = new Dictionary<char, int>();
dict['I'] = 1;
dict['V'] = 5;
dict['X'] = 10;
dict['L'] = 50;
dict['C'] = 100;
dict['D'] = 500;
dict['M'] = 1000;
Stack<char> st = new Stack<char>();
foreach (char ch in s.ToCharArray())
st.Push(ch);
int result = 0;
while (st.Count > 0)
{
var c1=st.Pop();
var ch1 = dict[c1];
if (st.Count > 0)
{
var c2 = st.Peek();
var ch2 = dict[c2];
if (ch2 < ch1)
{
result += (ch1 - ch2);
st.Pop();
}
else
{
result += ch1;
}
}
else
{
result += ch1;
}
}
return result;
}
这是我的解决方案:
/// <summary>
/// Converts a Roman number string into a Arabic number
/// </summary>
/// <param name="romanNumber">the Roman number string</param>
/// <returns>the Arabic number (0 if the given string is not convertible to a Roman number)</returns>
public static int ToArabicNumber(string romanNumber)
{
string[] replaceRom = { "CM", "CD", "XC", "XL", "IX", "IV" };
string[] replaceNum = { "DCCCC", "CCCC", "LXXXX", "XXXX", "VIIII", "IIII" };
string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
return Enumerable.Range(0, replaceRom.Length)
.Aggregate
(
romanNumber,
(agg, cur) => agg.Replace(replaceRom[cur], replaceNum[cur]),
agg => agg.ToArray()
)
.Aggregate
(
0,
(agg, cur) =>
{
int idx = Array.IndexOf(roman, cur.ToString());
return idx < 0 ? 0 : agg + arabic[idx];
},
agg => agg
);
}
/// <summary>
/// Converts a Arabic number into a Roman number string
/// </summary>
/// <param name="arabicNumber">the Arabic number</param>
/// <returns>the Roman number string</returns>
public static string ToRomanNumber(int arabicNumber)
{
string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
return Enumerable.Range(0, arabic.Length)
.Aggregate
(
Tuple.Create(arabicNumber, string.Empty),
(agg, cur) =>
{
int remainder = agg.Item1 % arabic[cur];
string concat = agg.Item2 + string.Concat(Enumerable.Range(0, agg.Item1 / arabic[cur]).Select(num => roman[cur]));
return Tuple.Create(remainder, concat);
},
agg => agg.Item2
);
}
以下是这些方法如何工作的说明:
转阿拉伯数字
第一个聚合步骤是替换罗马数字的特殊情况(例如:IV -> IIII)。第二个聚合步骤简单地总结了罗马字母的等效阿拉伯数字(例如 V -> 5)
ToRomanNumber:
我从给定的阿拉伯数字开始聚合。对于每一步,数字将除以罗马字母的等效数字。然后,该除法的其余部分是下一步的输入。除法结果将被转换为等效罗马数字字符,该字符将附加到结果字符串中。
FWIW,这是 David DeMar 答案的“尝试解析”版本:
private static readonly Dictionary<char, int> _romanMap = new Dictionary<char, int>() { { 'I', 1 }, { 'V', 5 }, { 'X', 10 }, { 'L', 50 }, { 'C', 100 }, { 'D', 500 }, { 'M', 1000 } };
public static bool TryParseRoman(string text, out int value)
{
value = 0;
if (string.IsNullOrEmpty(text))
return false;
var number = 0;
for (var i = 0; i < text.Length; i++)
{
if (!_romanMap.TryGetValue(text[i], out var num))
return false;
if ((i + 1) < text.Length)
{
if (!_romanMap.TryGetValue(text[i + 1], out var num2))
return false;
if (num < num2)
{
number -= num;
continue;
}
}
number += num;
}
value = number;
return true;
}
/*
this uses the string object Replace() & Split() methods
*/
int ToNumber(string roman){
/*
the 0 padding after the comma delimiter allows adding up the extra index produced by Split, which is not numerical
*/
string s1=roman.Replace("CM","900,0");
s1=s1.Replace("M","1000,0");
s1=s1.Replace("CD","400,0");
s1=s1.Replace("D","500,0");
s1=s1.Replace("XC","90,0");
s1=s1.Replace("C","100,0");
s1=s1.Replace("XL","40,0");
s1=s1.Replace("L","50,0");
s1=s1.Replace("IX","9,0");
s1=s1.Replace("X","10,0");
s1=s1.Replace("IV","4,0");
s1=s1.Replace("V","5,0");
s1=s1.Replace("I","1,0");
string[] spl=s1.Split(",");
int rlt=0;
for(int i=0;i<spl.Count();i++)
{
rlt+= Convert.ToInt32(spl[i].ToString());
}
return rlt;
}
我参考了这个博客。您可以将罗马数字反转,然后进行比较会更容易进行比较。
public static int pairConversion(int dec, int lastNum, int lastDec) { if (lastNum > dec) return lastDec - dec; 否则返回 lastDec + dec;}
public static int ConvertRomanNumtoInt(string strRomanValue)
{
var dec = 0;
var lastNum = 0;
foreach (var c in strRomanValue.Reverse())
{
switch (c)
{
case 'I':
dec = pairConversion(1, lastNum, dec);
lastNum = 1;
break;
case 'V':
dec=pairConversion(5,lastNum, dec);
lastNum = 5;
break;
case 'X':
dec = pairConversion(10, lastNum, dec);
lastNum = 10;
break;
case 'L':
dec = pairConversion(50, lastNum, dec);
lastNum = 50;
break;
case 'C':
dec = pairConversion(100, lastNum, dec);
lastNum = 100;
break;
case 'D':
dec = pairConversion(500, lastNum, dec);
lastNum = 500;
break;
case 'M':
dec = pairConversion(1000, lastNum, dec);
lastNum = 1000;
break;
}
}
return dec;
}
private static HashMap<Character, Integer> romanMap = new HashMap<>() {{
put('I', 1); put('V', 5); put('X', 10); put('L', 50);
put('C', 100); put('D', 500); put('M', 1000);
}};
private static int convertRomanToInt(String romanNumeral) {
int total = 0;
romanNumeral = romanNumeral.toUpperCase();
//add every Roman numeral
for(int i = 0; i < romanNumeral.length(); i++) {
total += romanMap.get(romanNumeral.charAt(i));
}
//remove the Roman numerals that are followed
//directly by a larger Roman numeral
for(int i = 0; i < romanNumeral.length()-1; i++) {
if(romanMap.get(romanNumeral.charAt(i))
< romanMap.get(romanNumeral.charAt(i+1))) {
total -= 2* romanMap.get(romanNumeral.charAt(i));
}
}
return total;
}
//note that the topmost solution does not solve this Roman numeral
//but mine does
//also note that this solution is a preference of simplicity over complexity
public static void main(String[] args) {
String rn = "CcLXxiV"; //274
System.out.println("Convert " + rn + " to " + convertRomanToInt(rn));
}
public static int ConvertRomanNumtoInt(string strRomanValue)
{
Dictionary RomanNumbers = new Dictionary
{
{"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}
};
int retVal = 0;
foreach (KeyValuePair pair in RomanNumbers)
{
while (strRomanValue.IndexOf(pair.Key.ToString()) == 0)
{
retVal += int.Parse(pair.Value.ToString());
strRomanValue = strRomanValue.Substring(pair.Key.ToString().Length);
}
}
return retVal;
}