Skip to content

Latest commit

 

History

History

number

Table of Contents generated with DocToc

数字格式转换问题

罗马数字 - 阿拉伯数字相互转换

罗马数字的特点

  1. 罗马数字共有7个,即I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)
  2. 重复数次:一个罗马数字重复几次,就表示这个数的几倍
  3. 同一数码最多只能连续出现三次,如40不可表示为XXXX,而要表示为XL
  4. 右加左减:
  • 在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字
  • 在较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字
  • 左减的数字有限制,仅限于IXC。比如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为例:

  1. 设置num = 1994,基准数base = 10
  2. 获取余数remainder = num % base = 1994 % 10 = 4,即一个小节,转换该小节:
  • 因为4 < 5 && 4 > 3,因此返回IV
  1. 更新num = num - remainder = 1994 - 4 = 1990,更新base = base * 10 = 100
  2. 获取余数remainder = num % base = 1990 % 100 = 90,即一个小节,转换该小节:
  • 因为90 > 50 && 90 < 100,因此返回XC
  1. 更新num = num - remainder = 1990 - 90 = 1900,更新base = base * 10 = 1000
  2. 获取余数remainder = num % base = 1900 % 1000 = 900,即一个小节,转换该小节:
  • 因为900 > 500 && 900 < 1000,因此返回CM
  1. 更新num = num - remainder = 1900 - 900 = 1000,更新base = base * 10 = 10000
  2. 获取余数remainder = num % base = 1000 % 10000 = 1000,即一个小节,转换该小节:
  • 因为1000 >= 1000,因此返回M
  1. 更新num = num - remainder = 1000 - 1000 = 0,结束遍历
  2. 合并小节的返回值得到结果MCMXCIV

从右到左依次遍历罗马字符。根据规则 4,如果当前字符代表的数字,小于右侧字符代表的数字,则应做减法;否则做加法。

MCMXCIV为例:

  1. 设置roma = MCMXCIVresult = 0,从最后一位i = roma.length - 1开始遍历
  2. i = 0时,roma[i] === V,数字 5 大于右侧数字(超范围),累加result += 5
  3. i = 1时,roma[i] === I,数字 1 小于右侧数字 5,累减result -= 1
  4. i = 2时,roma[i] === C,数字 100 大于右侧数字 1,累加result += 100
  5. i = 3时,roma[i] === X,数字 10 小于右侧数字 100,累减result -= 10
  6. i = 4时,roma[i] === M,数字 1000 大于右侧数字 10,累加result += 1000
  7. i = 5时,roma[i] === C,数字 100 小于右侧数字 1000,累减result -= 100
  8. i = 6时,roma[i] === M,数字 1000 大于右侧数字 100,累加result += 1000
  9. 遍历完成,返回结果1994

中文数字 - 阿拉伯数字相互转换

中文数字的特点

  1. 中文数字的权位和小节
  • 以一万,即 10000 为小节,每小节拥有一个权位,权位最低为,依次是亿万亿以下的小节没有权位。例如,
    • 数字20002020200每四个为一个小节,分为200 0202 0200,中文为二百亿零二百零二万零二百
    • 数字20001234分为2000 1234,中文为二千万一千二百三十四
  • 小节内部,拥有的权位。例如,数字1111本身是一个小节,中文为一千一百一十一
  • 小节内部的不能连续出现,但内部权位可以和外部的等权位连续出现。例如,二十万
  1. 中文数字内的零
  • 10000 为小节,小节结尾即便是0,也不使用。例如:1000一千
  • 小节内部,两个非零数字之间如果有 0,合并时要使用。例如,1070一千零七十
  • 当小节的位是 0 时,若本小节的左侧一小节无其他数字,则不用,否则就要用。例如,10700一万零七百,而0100则是一百

20002020202为例,遍历,分割成各个小节进行处理:

  1. 设置num = 20002020202
  2. 获取第一个小节,为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,且下一小节不是空,因此补零为零二百零二
  1. 获取第二个小节,为section = num % 10000 = 200,补齐为0202
  • 因为值和上一个小节一样,不再解释,返回二百零二
  • 获取该小节权位,为,则为二百零二万
  • 因为该小节千位是0,且下一小节不是空,因此补零为零二百零二万
  1. 获取第三个小节,为section = num % 10000 = 200
  • 处理小节内部,返回二百
  • 获取该小节权位,为亿,则为二百亿
  1. 合并前面的处理,得到二百亿零二百零二万零二百零二

中文数字转阿拉伯数字比较简单。遍历到权位之后进位即可。以二百亿零二百零二万零二百零二为例:

  1. 设置chinese = 二百亿零二百零二万零二百零二,结果result = 0,小节值的缓存栈tmp = [],然后从左侧i = 0开始遍历
  2. i = 0chinese[i] === 二,进入缓存栈tmp.push(2),此时tmp内为[2]
  3. i = 1chinese[i] === 百,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100),此时tmp内为[200]
  4. i = 2chinese[i] === 亿,则栈内数字累加后,乘以该小节权位,叠加到resultresult += tmp.reduce((n1, n2) => n1 + n2, 0) * 100000000,栈清空tmp = []
  5. i = 3chinese[i] === 零,跳过
  6. i = 4chinese[i] === 二,进入缓存栈tmp.push(2),此时tmp内为[2]
  7. i = 5chinese[i] === 百,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100),此时tmp内为[200]
  8. i = 6chinese[i] === 零,跳过
  9. i = 7chinese[i] === 二,进入缓存栈tmp.push(2),此时tmp内为[200, 2]
  10. i = 8chinese[i] === 万,则栈内数字累加后,乘以该小节权位,叠加到resultresult += tmp.reduce((n1, n2) => n1 + n2, 0) * 100000000,栈清空tmp = []
  11. i = 9chinese[i] === 零,跳过
  12. i = 10chinese[i] === 二,进入缓存栈tmp.push(2),此时tmp内为[2]
  13. i = 11chinese[i] === 百,则从栈中取出顶位数字,进位后入栈tmp.push(tmp.pop() * 100),此时tmp内为[200]
  14. i = 12chinese[i] === 零,跳过
  15. i = 13chinese[i] === 二,进入缓存栈tmp.push(2),此时tmp内为[200, 2]
  16. 遍历完成,返回result + tmp.reduce((n1, n2) => n1 + n2, 0)即最终结果20002020202