个人技术分享

最近遇到一个需要整数转罗马数字的问题

罗马数字的规则:

I   →     1
V   →     5
X   →     10
L   →     50
C   →     100
D   →     500
M   →     1000

字母从大到小依次排列,例如:8→XIII

但存在一种特殊情况,使得小字母在大字母右侧,来表示后者减去前者:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。


因此可以穷举出这几种情况,列为两个数组

再直接通过while暴力循环:

    public static string IntToRoman(int value)
    {
        int[] nums =new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        string[] romans = new string[]{ "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
        string result = "";
        while (value > 0)
        {
            for(int i = 0, l = nums.Length; i < l; ++i)
            {
                if (value >= nums[i])
                {
                    value -= nums[i];
                    result += romans[i];
                    break;
                }
            }
        }

        return result;
    }

后面我思考了一下,当value与第X位相减时,value必不可能大于x-1位,因此可以加一个变量用来记录位置,降低无效的循环次数;

public static string IntToRoman(int value)
    {
        int[] nums =new int[] { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        string[] romans = new string[]{ "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
        string result = "";
        int start=0;
        while (value > 0)
        {
            for(int i = start, l = nums.Length; i < l; ++i)
            {
                if (value >= nums[i])
                {
                    value -= nums[i];
                    result += romans[i];
                    start = i;
                    break;
                }
            }
        }

        return result;
    }
输入:8
方法1:VIII 循环次数46
方法2:VIII 循环次数12
输入:1994
方法1:MCMXCIV 循环次数17
方法2:MCMXCIV 循环次数11
输入:3999
方法1:MMMCMXCIX 循环次数15
方法2:MMMCMXCIX 循环次数9

可见改过之后的写法在整个值域(1-3999)内都优于原写法