Как преобразовать целочисленное значение в строку римских цифр?

Как я могу преобразовать целое число в его строковое представление римскими цифрами в C?


person Vishwanath Dalvi    schedule 13.02.2011    source источник
comment
Если вы не работаете в НФЛ, это должно быть домашним заданием, не так ли?   -  person jason    schedule 13.02.2011
comment
Ну не закидывая задание на SO и надеясь на код ;)   -  person    schedule 13.02.2011
comment
@delnan: видимо, все равно работает :-)   -  person 6502    schedule 13.02.2011
comment
@Jason - или какой-то проект локализации идет слишком далеко   -  person Martin Beckett    schedule 13.02.2011
comment
@Мартин Беккет: Ха-ха-ха. lt-это (латиница-Италия) локаль?   -  person jason    schedule 14.02.2011
comment
@jason search.cpan.org /~dconway/Lingua-Romana-Perligata-0.50/lib/   -  person Martin Beckett    schedule 14.02.2011


Ответы (2)


Самый простой способ, вероятно, настроить три массива для сложных случаев и использовать простую функцию, например:

// 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, выполняется простой поиск по трем таблицам, по одной для сотен, десятков и единиц. Например, возьмем случай, когда val равно 314.

val/100 в этом случае будет 3, поэтому поиск массива huns даст CCC, затем val = val % 100 даст вам 14 для поиска tens.

Тогда val/10 будет 1 в этом случае, поэтому поиск массива tens даст X, затем val = val % 10 даст вам 4 для поиска ones.

Тогда val будет 4 в этом случае, поэтому поиск массива ones даст IV.

Это дает вам CCCXIV за 314.


Версия с проверкой переполнения буфера — это простой шаг вперед:

// 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;
}

хотя на тот момент вы могли бы подумать о рефакторинге обработки сотен, десятков и единиц в отдельную функцию, поскольку они так похожи. Я оставлю это как дополнительное упражнение.

person paxdiablo    schedule 13.02.2011

не используйте предварительно рассчитанную карту для сложных случаев.

/* 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. должно быть легко понять, почему.

person j. andrew shusta    schedule 23.05.2017