web-dev-qa-db-fra.com

Comment convertir une valeur entière en chaîne en chiffres romains?

Comment convertir un entier en chaîne en chiffres romains en C?

20
mr_eclair

Le moyen le plus simple est probablement de configurer trois tableaux pour les cas complexes et d’utiliser une fonction simple comme:

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

Ceci gérera tout entier non signé, bien que les grands nombres aient énormément de caractères M à l'avant et que l'appelant s'assure que leur mémoire tampon est suffisamment grande.

Une fois que le nombre a été réduit à moins de 1000, il s'agit d'une simple recherche dans 3 tables, une pour les centaines, les dizaines et les unités. Par exemple, prenons le cas où val est 314.

val/100 sera 3 dans ce cas donc la recherche de tableau huns donnera CCC, puis val = val % 100 vous donnera 14 pour la recherche tens.

Alors val/10 sera 1 dans ce cas donc la recherche de tableau tens donnera X, puis val = val % 10 vous donnera 4 pour la recherche ones.

Alors val sera 4 dans ce cas donc la recherche de tableau ones donnera IV.

Cela vous donne CCCXIV pour 314.


Une version de contrôle de débordement de tampon est une étape simple à partir de là:

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

bien que, à ce moment-là, vous puissiez penser à refactoriser le traitement de centaines, dizaines et unités dans une fonction distincte car elles sont si similaires. Je vais laisser cela comme un exercice supplémentaire.

37
paxdiablo

n'utilisez pas de carte pré-calculée pour les cas difficiles.

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

vous n'avez pas non plus besoin de déclarer S. il devrait être facile de voir pourquoi.

1
j. andrew shusta
#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

Je pense que ValueConverter est l’une des méthodes les plus élégantes pour convertir un entier en chiffre romain. J'espère que Dante n'est pas trop en colère à propos de cela que je poste son code ici:

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