我该如何将整数转换为罗马数字?
function romanNumeralGenerator (int) {
}
例如,看下面的样例输入和输出:1 = "I"
5 = "V"
10 = "X"
20 = "XX"
3999 = "MMMCMXCIX"
注意:仅支持1到3999之间的数字。
我该如何将整数转换为罗马数字?
function romanNumeralGenerator (int) {
}
例如,看下面的样例输入和输出:1 = "I"
5 = "V"
10 = "X"
20 = "XX"
3999 = "MMMCMXCIX"
注意:仅支持1到3999之间的数字。
我在Google上发现了这篇博客,里面有一个不错的罗马数字转换器:
http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
function romanize (num) {
if (isNaN(num))
return NaN;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}
function romanize(num) {
var lookup = {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},roman = '',i;
for ( i in lookup ) {
while ( num >= lookup[i] ) {
roman += i;
num -= lookup[i];
}
}
return roman;
}
转载自2008年的评论,位置在:http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
for...in
。 - Oriolfor...in
循环中,对象的迭代顺序也保证与对象的属性顺序相同。回应@Oriol可能有点啰嗦,对象确实有“顺序”,自ES2015以来就是如此,但使用for...in
时它们的_迭代顺序_直到最近才被保证与属性顺序相同。 - Timothy J. Avenifunction convertToRoman(num) {
var roman = {
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
};
var str = '';
for (var i of Object.keys(roman)) {
var q = Math.floor(num / roman[i]);
num -= q * roman[i];
str += i.repeat(q);
}
return str;
}
0
,这意味着所有字母都已找到,我们可以退出递归。var romanMatrix = [
[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']
];
function convertToRoman(num) {
if (num === 0) {
return '';
}
for (var i = 0; i < romanMatrix.length; i++) {
if (num >= romanMatrix[i][0]) {
return romanMatrix[i][1] + convertToRoman(num - romanMatrix[i][0]);
}
}
}
这些函数将任何正整数转换为相应的罗马数字字符串;并将任何罗马数字转换为数字。
数字转罗马数字:
Number.prototype.toRoman= function () {
var num = Math.floor(this),
val, s= '', i= 0,
v = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
r = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
function toBigRoman(n) {
var ret = '', n1 = '', rem = n;
while (rem > 1000) {
var prefix = '', suffix = '', n = rem, s = '' + rem, magnitude = 1;
while (n > 1000) {
n /= 1000;
magnitude *= 1000;
prefix += '(';
suffix += ')';
}
n1 = Math.floor(n);
rem = s - (n1 * magnitude);
ret += prefix + n1.toRoman() + suffix;
}
return ret + rem.toRoman();
}
if (this - num || num < 1) num = 0;
if (num > 3999) return toBigRoman(num);
while (num) {
val = v[i];
while (num >= val) {
num -= val;
s += r[i];
}
++i;
}
return s;
};
罗马数字字符串转换为数字:
Number.fromRoman = function (roman, accept) {
var s = roman.toUpperCase().replace(/ +/g, ''),
L = s.length, sum = 0, i = 0, next, val,
R = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
function fromBigRoman(rn) {
var n = 0, x, n1, S, rx =/(\(*)([MDCLXVI]+)/g;
while ((S = rx.exec(rn)) != null) {
x = S[1].length;
n1 = Number.fromRoman(S[2])
if (isNaN(n1)) return NaN;
if (x) n1 *= Math.pow(1000, x);
n += n1;
}
return n;
}
if (/^[MDCLXVI)(]+$/.test(s)) {
if (s.indexOf('(') == 0) return fromBigRoman(s);
while (i < L) {
val = R[s.charAt(i++)];
next = R[s.charAt(i)] || 0;
if (next - val > 0) val *= -1;
sum += val;
}
if (accept || sum.toRoman() === s) return sum;
}
return NaN;
};
我个人认为最简洁的方法(不一定是最快的)是使用递归。
function convert(num) {
if(num < 1){ return "";}
if(num >= 40){ return "XL" + convert(num - 40);}
if(num >= 10){ return "X" + convert(num - 10);}
if(num >= 9){ return "IX" + convert(num - 9);}
if(num >= 5){ return "V" + convert(num - 5);}
if(num >= 4){ return "IV" + convert(num - 4);}
if(num >= 1){ return "I" + convert(num - 1);}
}
console.log(convert(39));
//Output: XXXIX
这只支持1-40的数字,但是按照这种模式可以轻松扩展。
这个版本不需要像其他版本那样为边缘情况(如4(IV),9(IX),40(XL),900(CM)等)
硬编码逻辑。
我已经对这段代码进行了1-3999的数据集测试,它可以正常工作。
简而言之;
这也意味着这个解决方案可以处理比罗马数字最大规模(3999)更大的数字。
看起来有一个交替规则来决定下一个主要的罗马数字字符。从I开始,乘以5得到下一个数字V,然后乘以2得到X,再乘以5得到L,然后乘以2得到C,以此类推,得到比例尺中的下一个主要数字字符。在这种情况下,假设“T”被添加到比例尺中以允许处理比3999更大的数字。为了保持相同的算法,“T”将代表5000。
I = 1
V = I * 5
X = V * 2
L = X * 5
C = L * 2
D = C * 5
M = D * 2
T = M * 5
代码:
function convertToRoman(num) {
//create key:value pairs
var romanLookup = {M:1000, D:500, C:100, L:50, X:10, V:5, I:1};
var roman = [];
var romanKeys = Object.keys(romanLookup);
var curValue;
var index;
var count = 1;
for(var numeral in romanLookup){
curValue = romanLookup[numeral];
index = romanKeys.indexOf(numeral);
while(num >= curValue){
if(count < 4){
//push up to 3 of the same numeral
roman.push(numeral);
} else {
//else we had to push four, so we need to convert the numerals
//to the next highest denomination "coloring-up in poker speak"
//Note: We need to check previous index because it might be part of the current number.
//Example:(9) would attempt (VIIII) so we would need to remove the V as well as the I's
//otherwise removing just the last three III would be incorrect, because the swap
//would give us (VIX) instead of the correct answer (IX)
if(roman.indexOf(romanKeys[index - 1]) > -1){
//remove the previous numeral we worked with
//and everything after it since we will replace them
roman.splice(roman.indexOf(romanKeys[index - 1]));
//push the current numeral and the one that appeared two iterations ago;
//think (IX) where we skip (V)
roman.push(romanKeys[index], romanKeys[index - 2]);
} else {
//else Example:(4) would attemt (IIII) so remove three I's and replace with a V
//to get the correct answer of (IV)
//remove the last 3 numerals which are all the same
roman.splice(-3);
//push the current numeral and the one that appeared right before it; think (IV)
roman.push(romanKeys[index], romanKeys[index - 1]);
}
}
//reduce our number by the value we already converted to a numeral
num -= curValue;
count++;
}
count = 1;
}
return roman.join("");
}
convertToRoman(36);
我知道这是一个老问题,但我对这个解决方案感到非常自豪 :) 它只处理小于1000的数字,但可以通过添加到“numeralCodes”二维数组来轻松扩展以包括任意大的数字。
var numeralCodes = [["","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
function convert(num) {
var numeral = "";
var digits = num.toString().split('').reverse();
for (var i=0; i < digits.length; i++){
numeral = numeralCodes[i][parseInt(digits[i])] + numeral;
}
return numeral;
}
<input id="text-input" type="text">
<button id="convert-button" onClick="var n = parseInt(document.getElementById('text-input').value);document.getElementById('text-output').value = convert(n);">Convert!</button>
<input id="text-output" style="display:block" type="text">
循环可能更优雅,但我觉得它们难以阅读。我想出了一个几乎硬编码的版本,这样看起来更加清晰易懂。只要你理解第一行,其余的就是小菜一碟。
function romanNumeralGenerator (int) {
let roman = '';
roman += 'M'.repeat(int / 1000); int %= 1000;
roman += 'CM'.repeat(int / 900); int %= 900;
roman += 'D'.repeat(int / 500); int %= 500;
roman += 'CD'.repeat(int / 400); int %= 400;
roman += 'C'.repeat(int / 100); int %= 100;
roman += 'XC'.repeat(int / 90); int %= 90;
roman += 'L'.repeat(int / 50); int %= 50;
roman += 'XL'.repeat(int / 40); int %= 40;
roman += 'X'.repeat(int / 10); int %= 10;
roman += 'IX'.repeat(int / 9); int %= 9;
roman += 'V'.repeat(int / 5); int %= 5;
roman += 'IV'.repeat(int / 4); int %= 4;
roman += 'I'.repeat(int);
return roman;
}
我创建了两个转换函数。
第一个函数使用reduce方法将数字转换为罗马数字。 第二个函数与第一个函数非常相似,该函数使用相同的方式来转换值。
你需要更改的是_roman
属性。因为你必须根据需要扩展这个常量的规模,我在那里放置了最大数量1000
,但你可以放更多。
更大规模的罗马数字可以在此处找到https://www.tuomas.salste.net/doc/roman/numeri-romani.html
const _roman = { 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 };
// 1903 => MCMIII
function toRoman(number = 0) {
return Object.keys(_roman).reduce((acc, key) => {
while (number >= _roman[key]) {
acc += key;
number -= _roman[key];
}
return acc;
}, '');
}
// MCMIII => 1903
function fromRoman(roman = '') {
return Object.keys(_roman).reduce((acc, key) => {
while (roman.indexOf(key) === 0) {
acc += _roman[key];
roman = roman.substr(key.length);
}
return acc;
}, 0);
}
console.log(toRoman(1903)); // should return 'MCMIII
console.log(fromRoman('MCMIII')); // should return 1903
NaN
或抛出异常,而不是返回false
。 - Onur Yıldırım715799999999
(715,799,999,999)。更大的数字要么没有返回值,要么(对于非常大的数字)会输出RangeError: Invalid array length
错误。除此之外,它完美地工作。谢谢! - tukusejssirs