web-dev-qa-db-ja.com

整数値をローマ数字の文字列に変換する方法は?

整数をCのローマ数字の文字列表現に変換するにはどうすればよいですか?

21
mr_eclair

最も簡単な方法は、複雑な場合のために3つの配列を設定し、次のような単純な関数を使用することです。

// convertToRoman:
//   In:  val: value to convert.
//        res: buffer to hold result.
//   Out: n/a
//   Cav: caller responsible for buffer size.

void convertToRoman (unsigned int val, char *res) {
    char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
    char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
    int   size[] = { 0,   1,    2,     3,    2,   1,    2,     3,      4,    2};

    //  Add 'M' until we drop below 1000.

    while (val >= 1000) {
        *res++ = 'M';
        val -= 1000;
    }

    // Add each of the correct elements, adjusting as we go.

    strcpy (res, huns[val/100]); res += size[val/100]; val = val % 100;
    strcpy (res, tens[val/10]);  res += size[val/10];  val = val % 10;
    strcpy (res, ones[val]);     res += size[val];

    // Finish string off.

    *res = '\0';
}

これは、すべての符号なし整数を処理しますが、大きな数の前には、非常に多くのM文字があり、呼び出し元は、それらのバッファーが十分に大きいことを確認する必要があります。

数が1000未満に減ると、それは単純な3つのテーブルのルックアップであり、数百、数十、および単位ごとに1つずつです。たとえば、val314である場合を考えます。

その場合、val/1003になるため、huns配列ルックアップはCCCを提供し、val = val % 100tensルックアップに対して14を提供します。

その場合、val/101になります。そのため、tens配列ルックアップはXを提供し、val = val % 10onesルックアップに対して4を提供します。

その場合、val4になります。そのため、ones配列ルックアップによりIVが得られます。

これにより、314CCCXIVが得られます。


バッファオーバーフローチェックバージョンは、そこからの簡単なステップです。

// convertToRoman:
//   In:  val: value to convert.
//        res: buffer to hold result.
//   Out: returns 0 if not enough space, else 1.
//   Cav: n/a

int convertToRoman (unsigned int val, char *res, size_t sz) {
    char *huns[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
    char *tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    char *ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
    int   size[] = { 0,   1,    2,     3,    2,   1,    2,     3,      4,    2};

    //  Add 'M' until we drop below 1000.

    while (val >= 1000) {
        if (sz-- < 1) return 0;
        *res++ = 'M';
        val -= 1000;
    }

    // Add each of the correct elements, adjusting as we go.

    if (sz < size[val/100]) return 0;
    sz -= size[val/100];
    strcpy (res, huns[val/100]);
    res += size[val/100];
    val = val % 100;

    if (sz < size[val/10]) return 0;
    sz -= size[val/10];
    strcpy (res, tens[val/10]);
    res += size[val/10];
    val = val % 10;

    if (sz < size[val) return 0;
    sz -= size[val];
    strcpy (res, ones[val]);
    res += size[val];

    // Finish string off.

    if (sz < 1) return 0;
    *res = '\0';
    return 1;
}

ただし、その時点では、数百、数十、単位の処理が非常に似ているため、別々の関数にリファクタリングすることを考えることができます。追加の練習として残しておきます。

37
paxdiablo

困難な場合には、弱々しい事前計算マップを使用しないでください。

/* roman.c */
#include <stdio.h>

/* LH(1) roman numeral conversion */
int RN_LH1 (char *buf, const size_t maxlen, int n)
{
  int S[]  = {    0,   2,   4,   2,   4,   2,   4 };
  int D[]  = { 1000, 500, 100,  50,  10,   5,   1 };
  char C[] = {  'M', 'D', 'C', 'L', 'X', 'V', 'I' };
  const size_t L = sizeof(D) / sizeof(int) - 1;
  size_t k = 0; /* index into output buffer */
  int i = 0; /* index into maps */
  int r, r2;

  while (n > 0) {
    if (D[i] <= n) {
      r = n / D[i];
      n = n - (r * D[i]);
      /* lookahead */
      r2 = n / D[i+1];
      if (i < L && r2 >= S[i+1]) {
        /* will violate repeat boundary on next pass */
        n = n - (r2 * D[i+1]);
        if (k < maxlen) buf[k++] = C[i+1];
        if (k < maxlen) buf[k++] = C[i-1];
      }
      else if (S[i] && r >= S[i]) {
        /* violated repeat boundary on this pass */
        if (k < maxlen) buf[k++] = C[i];
        if (k < maxlen) buf[k++] = C[i-1];
      }
      else
        while (r-- > 0 && k < maxlen)
          buf[k++] = C[i];
    }
    i++;
  }
  if (k < maxlen) buf[k] = '\0';
  return k;
}

/* gcc -Wall -ansi roman.c */
int main (int argc, char **argv)
{
  char buf[1024] = {'\0'};
  size_t len;
  int k;
  for (k = 1991; k < 2047; k++)
  {
    len = RN_LH1(buf, 1023, k);
    printf("%3lu % 4d %s\n", len, k, buf);
  }
  return 0;
}

実際にSを宣言する必要もありません。その理由は簡単にわかるはずです。

1
#ifndef numberof
    #define numberof(A) ((int)(sizeof(A)/sizeof((A)[0])))
#endif

static void roman(char *out, int max, int number)
{
    static int romanI[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
    static const char *romanA[] = {
        "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"
    };

    int i, v;

    out[0] = 0;
    for (i = 0; i < numberof(romanI); i++) {
        if (number <= 0)
            break;
        v = romanI[i];
        while (number >= v) {
            strcat_s(out, max, romanA[i]);
            number -= v;
        }
    }
}
0
dirck

ValueConverter は、整数をローマ数字に変換する最もエレガントな方法の1つだと思います。ここに彼のコードを投稿することについて、ダンテがそれほど怒っていないことを願っています。

public class RomanNumeralizer : IValueConverter
{
    private static IList<RomanNumeralPair> _Pairs;


    static RomanNumeralizer()
    {
        var list = new List<RomanNumeralPair>();

        list.Add(new RomanNumeralPair(1000, "M"));
        list.Add(new RomanNumeralPair(900, "CM"));
        list.Add(new RomanNumeralPair(500, "D"));
        list.Add(new RomanNumeralPair(400, "CD"));
        list.Add(new RomanNumeralPair(100, "C"));
        list.Add(new RomanNumeralPair(90, "XC"));
        list.Add(new RomanNumeralPair(50, "L"));
        list.Add(new RomanNumeralPair(40, "XL"));
        list.Add(new RomanNumeralPair(10, "X"));
        list.Add(new RomanNumeralPair(9, "IX"));
        list.Add(new RomanNumeralPair(5, "V"));
        list.Add(new RomanNumeralPair(4, "IV"));
        list.Add(new RomanNumeralPair(1, "I"));

        _Pairs = list.AsReadOnly();
    }


    private IList<RomanNumeralPair> PairSet
    {
        get
        {
            return _Pairs;
        }
    }


    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ConvertToRomanNumeral(System.Convert.ToInt32(value));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }


    private string ConvertToRomanNumeral(int input)
    {
        StringBuilder myBuilder = new StringBuilder();

        foreach (RomanNumeralPair thisPair in _Pairs)
        {
            while (input >= thisPair.Value)
            {
                myBuilder.Append(thisPair.RomanValue);
                input -= thisPair.Value;
            }
        }

        return myBuilder.ToString();
    }
}

public class RomanNumeralPair
{
    private string _RomanValue;
    private int _Value;


    public RomanNumeralPair(int value, string stringValue)
    {
        this._Value = value;
        this._RomanValue = stringValue;
    }


    public string RomanValue
    {
        get
        {
            return this._RomanValue;
        }
    }

    public int Value
    {
        get
        {
            return this._Value;
        }
    }
}
0
Oliver