Table of Contents generated with DocToc
- 罗马数字共有7个,即I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)
- 重复数次:一个罗马数字重复几次,就表示这个数的几倍
- 同一数码最多只能连续出现三次,如
40
不可表示为XXXX
,而要表示为XL
- 右加左减:
- 在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字
- 在较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字
- 左减的数字有限制,仅限于
I
、X
、C
。比如45
不可以写成VL
,只能是XLV
- 但是,左减时不可跨越一个位值。比如,
99
不可以用IC
表示,而是用XCIX
表示 - 左减数字最多为一位,比如
8
写成VIII
,而非IIX
- 右加数字不可连续超过三位,比如
14
写成XIV
,而非XIIII
罗马数字每三组字母是一个权位范围,例如,I, V, X
可代表[1, 10]
内数字,X, L, C
可代表[10, 100]
内数字,C, D, M
可代表[100, 1000]
范围数字。对于1000
以上,暂且只增加M
个数。由此可得规律。
以1994
为例:
- 设置
num = 1994
,基准数base = 10
- 获取余数
remainder = num % base = 1994 % 10 = 4
,即一个小节,转换该小节:
- 因为
4 < 5 && 4 > 3
,因此返回IV
- 更新
num = num - remainder = 1994 - 4 = 1990
,更新base = base * 10 = 100
- 获取余数
remainder = num % base = 1990 % 100 = 90
,即一个小节,转换该小节:
- 因为
90 > 50 && 90 < 100
,因此返回XC
- 更新
num = num - remainder = 1990 - 90 = 1900
,更新base = base * 10 = 1000
- 获取余数
remainder = num % base = 1900 % 1000 = 900
,即一个小节,转换该小节:
- 因为
900 > 500 && 900 < 1000
,因此返回CM
- 更新
num = num - remainder = 1900 - 900 = 1000
,更新base = base * 10 = 10000
- 获取余数
remainder = num % base = 1000 % 10000 = 1000
,即一个小节,转换该小节:
- 因为
1000 >= 1000
,因此返回M
- 更新
num = num - remainder = 1000 - 1000 = 0
,结束遍历 - 合并小节的返回值得到结果
MCMXCIV
从右到左依次遍历罗马字符。根据规则 4,如果当前字符代表的数字,小于右侧字符代表的数字,则应做减法;否则做加法。
以MCMXCIV
为例:
- 设置
roma = MCMXCIV
,result = 0
,从最后一位i = roma.length - 1
开始遍历 i = 0
时,roma[i] === V
,数字 5 大于右侧数字(超范围),累加result += 5
i = 1
时,roma[i] === I
,数字 1 小于右侧数字 5,累减result -= 1
i = 2
时,roma[i] === C
,数字 100 大于右侧数字 1,累加result += 100
i = 3
时,roma[i] === X
,数字 10 小于右侧数字 100,累减result -= 10
i = 4
时,roma[i] === M
,数字 1000 大于右侧数字 10,累加result += 1000
i = 5
时,roma[i] === C
,数字 100 小于右侧数字 1000,累减result -= 100
i = 6
时,roma[i] === M
,数字 1000 大于右侧数字 100,累加result += 1000
- 遍历完成,返回结果
1994
- 中文数字的权位和小节
- 以一万,即 10000 为小节,每小节拥有一个权位,权位最低为
万
,依次是万
,亿
,万亿
。万
以下的小节没有权位。例如,- 数字
20002020200
每四个为一个小节,分为200 0202 0200
,中文为二百亿零二百零二万零二百
- 数字
20001234
分为2000 1234
,中文为二千万一千二百三十四
- 数字
- 小节内部,拥有
十
、百
、千
的权位。例如,数字1111
本身是一个小节,中文为一千一百一十一
- 小节内部的
十
、百
、千
不能连续出现,但内部权位可以和外部的万
等权位连续出现。例如,二十万
- 中文数字内的零
- 10000 为小节,小节结尾即便是
0
,也不使用零
。例如:1000
是一千
- 小节内部,两个非零数字之间如果有 0,合并时要使用
零
。例如,1070
是一千零七十
- 当小节的
千
位是 0 时,若本小节的左侧一小节无其他数字,则不用零
,否则就要用零
。例如,10700
是一万零七百
,而0100
则是一百
以20002020202
为例,遍历,分割成各个小节进行处理:
- 设置
num = 20002020202
- 获取第一个小节,为
section = num % 10000 = 202
,补齐为0202
- 处理小节内部,从左边第一位开始遍历;
i = 0
时,section[i] === 0
:跳过i = 1
时,section[i] === 2
:此时为百位,获取到匹配为二百
i = 2
时,section[i] === 0
:跳过i = 3
时,section[i] === 2
:此时为个位,且根据规则 - 两个非零数字之间如果有 0,合并时要使用零
,获取到匹配为零二
- 小节内部处理完成,返回
二百零二
- 获取该小节权位,为空字符串,则值不变,还是
二百零二
- 因为该小节千位是
0
,且下一小节不是空,因此补零为零二百零二
- 获取第二个小节,为
section = num % 10000 = 200
,补齐为0202
- 因为值和上一个小节一样,不再解释,返回
二百零二
- 获取该小节权位,为
万
,则为二百零二万
- 因为该小节千位是
0
,且下一小节不是空,因此补零为零二百零二万
- 获取第三个小节,为
section = num % 10000 = 200
- 处理小节内部,返回
二百
- 获取该小节权位,为
亿
,则为二百亿
- 合并前面的处理,得到
二百亿零二百零二万零二百零二
中文数字转阿拉伯数字比较简单。遍历到权位之后进位即可。以二百亿零二百零二万零二百零二
为例:
- 设置
chinese = 二百亿零二百零二万零二百零二
,结果result = 0
,小节值的缓存栈tmp = []
,然后从左侧i = 0
开始遍历 i = 0
,chinese[i] === 二
,进入缓存栈tmp.push(2)
,此时tmp
内为[2]
i = 1
,chinese[i] === 百
,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100)
,此时tmp
内为[200]
i = 2
,chinese[i] === 亿
,则栈内数字累加后,乘以该小节权位,叠加到result
:result += tmp.reduce((n1, n2) => n1 + n2, 0) * 100000000
,栈清空tmp = []
i = 3
,chinese[i] === 零
,跳过i = 4
,chinese[i] === 二
,进入缓存栈tmp.push(2)
,此时tmp
内为[2]
i = 5
,chinese[i] === 百
,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100)
,此时tmp
内为[200]
i = 6
,chinese[i] === 零
,跳过i = 7
,chinese[i] === 二
,进入缓存栈tmp.push(2)
,此时tmp
内为[200, 2]
i = 8
,chinese[i] === 万
,则栈内数字累加后,乘以该小节权位,叠加到result
:result += tmp.reduce((n1, n2) => n1 + n2, 0) * 100000000
,栈清空tmp = []
i = 9
,chinese[i] === 零
,跳过i = 10
,chinese[i] === 二
,进入缓存栈tmp.push(2)
,此时tmp
内为[2]
i = 11
,chinese[i] === 百
,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100)
,此时tmp
内为[200]
i = 12
,chinese[i] === 零
,跳过i = 13
,chinese[i] === 二
,进入缓存栈tmp.push(2)
,此时tmp
内为[200, 2]
- 遍历完成,返回
result + tmp.reduce((n1, n2) => n1 + n2, 0)
即最终结果20002020202