Partage

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

19 février 2009 à 20:34:17

Une question, on doit aussi faire un convertisseur chiffre arabe -> chiffre romain ou pas?
(Au fait, On pourrait-utiliser ca en crypto en poussant un peu plus :D )
19 février 2009 à 21:09:04

Citation : yudassen

Une question, on doit aussi faire un convertisseur chiffre arabe -> chiffre romain ou pas?
(Au fait, On pourrait-utiliser ca en crypto en poussant un peu plus :D )



C'est pas dit je crois (et convertir romain -> arabe en gérant les erreurs, c'est déjà ça :D ).
19 février 2009 à 23:46:39

Citation : Nanoc

Niveau 1




Comme vous pouvez vous en douter, votre programme devra "traduire" un nombre entier en chiffre romain (valide) et afficher le résultat dans la console.




<édit> Juste une petite question sur le niveau 2.

Si le nombre romain entré est XDD, par exemple, il viole la règle 1 (pas en ordre décroissant) et la règle 5 (trop long). Dans ce cas, le programme doit-il afficher que le nombre XDD viole la règle 1 et la règle 5? Ou s'il préciser qu'il viole la règle 5 est suffisant?
22 février 2009 à 19:38:22

Je pense que tu dis ce tout ce qui ne va pas (genre : Votre nombre XDD ne convient pas, car votre écriture est trop longue, et que les symboles doivent être données par ordre décroissant. Voici la correction : MX.

Votre nombre est : 1010. )

Sinon, si le programme a une erreur, doit-on quand même donner le résultat (ou, s'il y en a deux ?), et une correction ?

Voilà :)

PS : petit hs mais bon... bravo pour ton poste de validateur ^^

Edit : quelques fautes :honte: ...
23 février 2009 à 0:57:49

Indiquer l'erreur la plus grave (comme le fait le compilateur C++).
La règle numéro 1 étant plus importante que la 2 qui est elle-même plus importante que la 3 etc.

Après, tu peux donner un résultat mais suivant l'erreur, on ne peut pas savoir ce que cela voulait réelement dire. Donc non.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
25 février 2009 à 0:29:36

Ok merci.

Et, s'il est possible, on donne une correction?
Comme ceci?
XXXX
Chiffre invalide. Il viole la regle 5.
On pourrait l'ecrire de maniere plus courte.
Le resultat serait : XL
Objet invalide

Ou alors, comme ceci?
XXXX
Chiffre invalide. Il viole la regle 5.
On pourrait l'ecrire de maniere plus courte.
Le resultat serait : XL
Nombre romains => XL
Nombre arabe ==> 40
5 mars 2009 à 17:34:07

Bonjour j'ai un petit problème je pense que je me suis perdu dans les fonction.
lorsque je fait ceci
#include <iostream>

using namespace std;

int changerNombre(char nombreRomain[]);

int main()

le compilateur me dit
main.cpp:5: error: too few arguments to function `int changerNombre(char*)'

Mais je ne comprend pas pourquoi je lui passe nombreRomain en argument quand même...

pourriez vous m'aider.
5 mars 2009 à 17:38:50

Citation : kikitoutdur

Bonjour j'ai un petit problème je pense que je me suis perdu dans les fonction.
lorsque je fait ceci

#include <iostream>

using namespace std;

int changerNombre(char nombreRomain[]);

int main()


le compilateur me dit

main.cpp:5: error: too few arguments to function `int changerNombre(char*)'


Mais je ne comprend pas pourquoi je lui passe nombreRomain en argument quand même...

pourriez vous m'aider.



Premièrement, tu devrais ouvrir un nouveau sujet, ici, c'est pour discuter des exercices, pas pour résoudre les problèmes.

Sinon, peut-on avoir la ligne ou tu implémentes ta fonction?
5 mars 2009 à 17:41:02

ok excuse moi d'avoir posté ici je vais ouvrir un nouveau post
5 mars 2009 à 17:46:24

a- Ton Exemple est certes Minimal, mais aussi totalement inComplet.
b- Ce n'est pas le bon endroit pour poser des questions
c- Le but est de manipuler le C++, donc par extension std::string, et non les chaines C zéro-terminées.
d- Une fonction qui attend un argument, on l'appelle en lui refilant un paramètre.
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
Anonyme
28 mars 2009 à 9:55:32

Bonjour tout le monde est il possible de proposer notre résolution de l'exercice dans un langage autre que c++ (c#) ?
29 mars 2009 à 1:10:44

Après une longue absence, voici la suite de l'aventure. Cet exercice est volontairement plus difficile que le précédent.

Exercice du mois d'avril 2009



Nom : Polynômes
Sujet : Classes, surcharge d'opérateurs, algorithme


Lorsque l'on fait du calcul numérique (simulation de choses par un ordinateur), on a souvent besoin de représenter des objets mathématiques. Le cas des fractions a déjà été traité, il est temps de s'attaquer au cas des polynômes.
Dans le cadre de cet exercice, on considèrera la définition suivante d'un polynôme.

Un polynome est une somme de puissances où chaque terme est pondéré par un coefficient.

<math>\(P(x) = \sum_{i=0}^n a_i x^i = a_0 + a_1 \cdot x + a_2 \cdot x^2 + ~\dots~ + a_n \cdot x^n\)</math>

Les coefficients <math>\(a_1, a_2, \dots, a_n\)</math> ainsi que <math>\(x\)</math> sont des nombres réels. Le nombre entier <math>\(n\)</math> est appelé le degré du polynôme.

Les expressions suivantes sont des polynomes:
  • <math>\(1 + 3x +x^2\)</math> de degrè 2
  • <math>\(x^2 + 8.5 x^{13}\)</math> de degré 13
  • <math>\(-34 x^4\)</math> de degré 4
  • <math>\(4\)</math> de degré 0


Les expressions suivantes ne sont pas des polynomes:
  • <math>\(x^{3.5}\)</math> (puissance non-entière)
  • <math>\(3 + x^4y\)</math> (variable <math>\(y\)</math>)


L'évaluation d'un polynome consiste à calculer sa valeur pour un nombre <math>\(x\)</math> donné. Par exemple si <math>\(P(x) = x^2 - 2x + 12\)</math>, l'évaluation du polynôme en <math>\(x=3\)</math> donne:

<math>\(P(3) = 3^2 - 2\cdot3 + 12 = 15\)</math>

On dit que deux polynômes sont égaux si tous leurs coefficients sont égaux un par un.

On définit des opérations sur les polynomes de la manière suivante:
L'addition et la soustraction se font en additionant et soustrayant terme par terme. Par exemple:
<math>\((x^2 - 2x + 12) + (1 + 3x + x^2) = 2x^2 + x + 13\)</math>
<math>\((x^2 - 2x + 12) - (1 + 3x + x^2) = 5x + 11\)</math>

La multiplication s'effectue en utilisant les règles usuelles de l'algèbre.
<math>\((x^2 - 2x + 12) \cdot (1 + 3x + x^2) = x^4 + x^3 + 7x^2 + 34x + 12\)</math>
Ce qui peut devenir très vite fastidieux si le degré du polynôme est grand. (Voilà une bonne raison d'implémenter une telle classe.

Le cas de la division est plus complexe. Comme pour les entiers, il s'agit d'une division avec reste.
Cela se fait en utilisant une division Euclidienne.
Si d'autres explications sont nécessaires, merci de me le dire et je développerai.


L'exercice



Le but de cet exercice est de réaliser une classe Polynome qui permettera de représenter la notion de polynome mathématique.

Niveau 1



Votre classe devra fournir les services suivants:

1) Construction par défaut créant un polynome de degré 0 avec 0 comme seul coefficient.

2) Construction à partir d'un nombre réel (polynôme de degré 0)

3) Surcharge des opérateurs arithmétiques usuels (+,-,*). Pensez également à implémenter les versions +=,-= et *= des opérateurs. Vous pouvez également réfléchir à la question des opérateurs du type "Polynome + Reel" ou "Reel + Polynome".

4) Surcharger l'opérateur << pour l'affichage dans un flux.

Votre Polynome devra toujours s'afficher en ne montrant pas les termes dont les coefficients sont 0. Sauf si le Polynome est le Polynome de degré 0 avec seulement 0 comme coefficient.


6) Surcharger les opérateurs de comparaison (==,!=).

7) Proposer une fonction "evaluer" qui permet d'évaluer le Polynome en un point x donné.

8) Proposer un moyen de connaître le degré du Polynome.

9) Proposer un moyen de connaître et de modifier chacun des coefficients du Polynome.

Niveau 2



Ce niveau consiste à ajouter la division euclidienne de polynômes. Pour cela, il vous faudra implémenter les opérateurs /,%,/= et %=.

Niveau 3



Pour le niveau 3, je vous propose de généraliser la notion de polynôme à n'importe quel type et pas seulement aux réels. Pour cela, quoi de vieux que d'utiliser les templates ?
Votre classe devra répondre au critère du niveau 2 tout en étant implémentée de sorte qu'elle soit utilisable avec n'improte quel type d'objet (mathématique).
Elle devra en particulier fonctionner avec les types suivants: int, Fraction, BigInt ou encore la classe complex de la SL.

Comment commencer ?



Si vous ne savez pas commencer, lisez les différents messages secrets.

Les coefficients forment une liste de nombres. Pourquoi ne pas les représenter sous forme de tableau ?


Commencez par implémenter l'opérateur << afin de vérifier ce qui se passe.


La division (et modulo) est le plus difficile, faites le reste en premier.


Pour toute information, je vous laisse revoir vos cours de maths ou Wikipedia par exemple. Vous pouvez aussi poser vos questions dans ce thread si la donnée n'est pas assez claire.

Vous avez jusqu'au 30 avril pour soumettre vos réponses à Réponse_Exercices.

Bonne chance à tous !
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
29 mars 2009 à 1:12:11

Les corrections manquantes vont arriver incessament sous peu.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
29 mars 2009 à 1:31:12

Il y a juste un truc qui m'intrigue pour les polynômes, il s'agit de la comparaison de deux polynômes.

Pour comparer deux flottants, je crois qu'il est impossible d'utiliser == et !=, il faut se donner une erreur.
Mais comment généraliser ça à un type template ?
29 mars 2009 à 10:28:25

J'ai absolument rien pigé. Suite de puissances, ok, ça peut aller, mais pondérées pas un coefficient... eh ben je laisse tomber cet exo, trop dur pour moi (c'est quel niveau ça ? première, un truc comme ça non ?)

Bonne chance aux participants :)
29 mars 2009 à 11:09:16

Citation : Cyprien_

Il y a juste un truc qui m'intrigue pour les polynômes, il s'agit de la comparaison de deux polynômes.

Pour comparer deux flottants, je crois qu'il est impossible d'utiliser == et !=, il faut se donner une erreur.
Mais comment généraliser ça à un type template ?



Théoriquement, la classe Polynome ne fera qu'utiliser l'opérateur == surchargé de la classe utilisée comme type. Le problème est donc reporté vers la classe utilisée comme type.
Pour les types POD (double, float, ...), on peut toujours spécialiser la fonction.

Citation : Germanof

J'ai absolument rien pigé. Suite de puissances, ok, ça peut aller, mais pondérées pas un coefficient... eh ben je laisse tomber cet exo, trop dur pour moi (c'est quel niveau ça ? première, un truc comme ça non ?)



C'est plus difficile que les autres oui. J'ai aucune idée de quand on apprend ce qu'est un polynôme en France, mais à part la division, il n'y a pas grand chos ede compliqué si tu regardes les exemples de Wikipédia pour t'aider.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
29 mars 2009 à 12:05:06

Les polynomes dans R[X] ou dans C[X], c'est bac +1, mais l'exo est accessible à un élève de première ou terminale, sauf p-e pour la division de polynome.
29 mars 2009 à 12:14:20

Mmmh. Mettons la division dans un niveau intermédiaire alors.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
29 mars 2009 à 12:34:55

BAC+1 ??
C'est ma mémoire, ou le programme de maths qui s'est désagrégé ?
C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
29 mars 2009 à 12:38:50

J'ai aussi fait ça largement avant en Suisse mais bon les programmes différent pas mal.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
29 mars 2009 à 13:24:21

lmghs >> je confirme. En 1er S, on voit les fonctions polynomiales de degré 2 à coefficients réels avec des solutions dans R.
En Ts, les solutions sont abordées dans C mais les coeffs restent réels. C'est en sup que les polynomes sont vraiment abordés avec de vrais définitions, l'arithmétique des polynomes, tous les trucs fun ^^
29 mars 2009 à 15:37:12

Ah ouais... ben avec un peu de chance, je pourrais faire cet exo dans 3-4 ans :)
29 mars 2009 à 15:42:45

Pour moi, Lyon, les polynômes je les ai vu en 1ère dans R, puis en terminale pour C.
On n'a abordé la division qu'en BAC + 1 (1er semestre)

Sinon, sympa l'exercice
Anonyme
29 mars 2009 à 16:22:39

Citation : Darkelfe

Pour moi, Lyon, les polynômes je les ai vu en 1ère dans R, puis en terminale pour C.
On n'a abordé la division qu'en BAC + 1 (1er semestre)

Sinon, sympa l'exercice

*

Nan, en 1er, tu as vu en 1er des fonctions polynomiale de degré 2 à coefficients réels à valeur dans R.
Ce n'est pas un polynome de degré N dans R[X] ou dans C[X]

Regarde en détail ici ce qu'on fait avec des polynomes.
29 mars 2009 à 20:09:13

Je sais de quoi je parle... Je viens de vérifier, j'ai vu des polynôme à degrée N (d'une manière générale, mais plus détaillé pour les 2 premiers degrés) dans C en terminale
29 mars 2009 à 20:22:54

Citation : Darkelfe

Je sais de quoi je parle... Je viens de vérifier, j'ai vu des polynôme à degrée N (d'une manière générale, mais plus détaillé pour les 2 premiers degrés) dans C en terminale


A moins que t'aies 50 ans ou plus :D , ça m'étonnerait qu'il ya ait quoi que ce soit de bien consistant... T'as quoi dans ton cours de Term sur les polynomes de degré quelconque ? La définition d'une fonction polynomiale ? Doit pas y avoir grand chose de plus, t'arriveras pas à me faire croire que t'as fait de l'arithmétique dans <math>\(\mathbb{C}[X]\)</math> en terminale...
31 mars 2009 à 13:07:21

Sujet très intéressant, je pense que je vais participer pour une fois !
Je sais pas trop où tu vas chercher tes idées Nanoc mais chapeau, c'est pas toujours évident de trouver des truc sympa à faire.

Pour ce qui est des études, je suis d'acc on définit rapidement ce qu'est un polynômes en 1er mais on n'étudie vraiment que ce de degré 2.
31 mars 2009 à 17:01:53

Solution du mois de février 2009



Bonjour tout le monde ! Il est temps que je dévoile après beaucoup de retard, une solution pour l'exercice du mois de février. Vous avez été 22 à m'envoyer une solution. Parmi celles reçues, plusieurs n'effectuaient pas les conversions de manières correctes et pour les autres, c'était bien trop complexe même si cela fonctionnait.

Voici une solution possible.

Partie "Papier-Crayon"



Comme c'est principalement un exercice d'algorithmique, il est important de commencer par réféchir avec une feuille de papier avant de commencer à taper quoi que ce soit sur son clavier.

Des nombres arabes vers les nombres romains...

Il y a plusieurs approches possibles à ce problème. J'en ai choisi une qui me paraît simple à comprendre. On effectue le tout en 2 étapes.
Premièrement on convertit "bêtement" le nombre arabe en une chaîne de symboles romains sans tenir compte des règles de soustraction ni des symboles 500, 50 et 5. Cela donne par exemple:

3842 -> MMMCCCCCCCCXXXXII

On passe ensuite dans une étape de simplification qui va remplacer les longues chaines par des soustractions et/ou remplacer 5 symboles consécutifs par le symbole correspondant. Par exemple:

CCCCCCC -> DCC
XXXX -> XL
IIIIIIIII -> IX

Notre exemple initial devient donc:

3842 -> MMMCCCCCCCCXXXXII -> MMMDCCCXLII

De cette manière, on a toujours la solution correcte et la plus courte.

...et le contraire

Le sens opposé est un petit peu plus compliqué si l'on veut tenir compte du fait que l'utilisateur peut faire des erreurs.
La première chose à vérifier et que l'utilisateur n'a pas rentré de symboles non-autorisés.

XXCCDM -> Ok (pour le moment)
CCCA -> Erreur

Ensuite, on va parcourir la chaine de droite à gauche et comparer chaque "symbole" avec celui qui est à sa droite (si il y en a un). Le caractère courant est représenté en rouge.

MMCXII

Si le symbole a une valeur plus élevée ou égale à son voisin de droite et que la valeur n'est pas plus petite que tout les symboles placés à droite, alors c'est OK. (Rappellez-vous que les symboles doivent être placé dans l'ordre décroissant)

MMCXII -> X est plus grand que I -> Ok
CCM -> C est égal à C MAIS est plus petit que M -> Ordre non-décroissant
MMXM -> X est plus petit que M -> voir la suite

Si par contre le symbole a une valeur inférieur, c'est soit que l'utilisateur a fait une erreur ou alors on a une soustraction.
On vérifie alors que le symbole est un des 6 cas de soustraction autorisé (IV, IX, XL, XC, CD, CM)

MMXM -> XM -> Erreur
MMCM -> CM -> Ok

Et on passe au caractère suivant.

Choix des structures de données



Pour contenir un nombre romain, quoi de mieux qu'une simple chaîne de caractères (std::string) ?
Pour contenir les valeurs de chaques symboles, j'ai choisi d'utiliser un tableau d'entier.

const string SYMBOLES = "MDCLXVI";
const int NOMBRES_SYMBOLES = 7;
const int VALEURS_SYMBOLES[NOMBRES_SYMBOLES] = {1000, 500, 100, 50, 10, 5, 1};


Programmation



Il est temps de passer à la programmation, ce qui revient à traduire ce qu'on a écrit ci-dessus en C++. J'ai choisi de jouer avec les exceptions pour signaler les erreurs.

Des nombres arabes vers les nombres romains...

La première partie est simple et je pense ne pas avoir besoin de l'expliquer.

string arabToRoman(int arabe)
{
    //On commence par tester si le nombre est valide
    if (arabe < 1 || arabe > 4999)
        throw string("Le nombre doit être compris entre 1 et 4999");

    string romain;  //La chaine que l'on va renvoyer
    int i;	    //Compteur

    //On compte le nombre de 'M' à écrire
    for (i = 0; i<arabe/1000; ++i)
        romain += "M";

    //On supprime les milliers du nombre. A partir de la, arabe est <1000
    arabe -= i*1000;

    //On compte le nombre de 'C' à écrire
    for (i = 0; i<arabe/100; ++i)
        romain += "C";

    //On supprime les centaines du nombre. A partir de la, arabe est <100
    arabe -= i*100;

    //On compte le nombre de 'X' à écrire
    for (i = 0; i<arabe/10; ++i)
        romain += "X";

    //On supprime les dizaines du nombre. A partir de la, arabe est <10
    arabe -= i*10;

    //On compte le nombre de 'I' à écrire
    for (i = 0; i<arabe; ++i)
        romain += "I";


A partir de là, il reste à simplifier. Pour ce faire, il va falloir compter le nombre de "C" qui se suivent et remplacer par la chaîne correspondante. Puis recommencer avec "X" et "I".
Pour s'aider à la tâche, on utilise les fonctions membres de la classe string.

La fonction rfind cherche la dernière occurence d'un symbole et la fonction find renvoit la première occurence. La différence entre les deux nous donne le nombre de symboles.
La fonction replace remplace une partie de la chaîne de longueur passée en argument par une autre chaîne.

//Simplification ---------------------------------------------------------

    //On remplace les "C" par ce qu'il faut pour simplifier
    switch (romain.rfind('C') - romain.find('C') + 1)
    {
    case 9:
        romain.replace(romain.find('C'), 9, "CM");
        break;
    case 8:
        romain.replace(romain.find('C'), 8, "DCCC");
        break;
    case 7:
        romain.replace(romain.find('C'), 7, "DCC");
        break;
    case 6:
        romain.replace(romain.find('C'), 6, "DC");
        break;
    case 5:
        romain.replace(romain.find('C'), 5, "D");
        break;
    case 4:
        romain.replace(romain.find('C'), 4, "CD");
        break;
    default:
        break;
    }

    //On remplace les "X" par ce qu'il faut pour simplifier
    switch (romain.rfind('X') - romain.find('X') + 1)
    {
    case 9:
        romain.replace(romain.find('X'), 9, "XC");
        break;
    case 8:
        romain.replace(romain.find('X'), 8, "LXXX");
        break;
    case 7:
        romain.replace(romain.find('X'), 7, "LXX");
        break;
    case 6:
        romain.replace(romain.find('X'), 6, "LX");
        break;
    case 5:
        romain.replace(romain.find('X'), 5, "L");
        break;
    case 4:
        romain.replace(romain.find('X'), 4, "XL");
        break;
    default:
        break;
    }

    //On remplace les "I" par ce qu'il faut pour simplifier
    switch (romain.rfind('I') - romain.find('I') + 1)
    {
    case 9:
        romain.replace(romain.find('I'), 9, "IX");
        break;
    case 8:
        romain.replace(romain.find('I'), 8, "VIII");
        break;
    case 7:
        romain.replace(romain.find('I'), 7, "VII");
        break;
    case 6:
        romain.replace(romain.find('I'), 6, "VI");
        break;
    case 5:
        romain.replace(romain.find('I'), 5, "V");
        break;
    case 4:
        romain.replace(romain.find('I'), 4, "IV");
        break;
    default:
        break;
    }

    //Et on renvoit le resultat
    return romain;
}


Vous remarquerez que les deux morceaux de code présentés sont très répétitifs et que la seule différence entre les différents cas est le symbole et la valeur de ce dernier. On peut donc raccourcir le code précédent en utilisant les constantes que nous avons définies précédemment.

string arabToRoman(int arabe)
{
    //On commence par tester si le nombre est valide
    if (arabe < 1 || arabe > 4999)
        throw string("Le nombre doit être compris entre 1 et 4999");

    //La chaine que l'on va renvoyer
    string romain;

    for (int j=0; j<NOMBRES_SYMBOLES; j+=2)
    {
        int i=0;

        //On compte le nombre de symboles d'un type donné à écrire
        for (i = 0; i<arabe/VALEURS_SYMBOLES[j]; ++i)
            romain += SYMBOLES[j];

        //On supprime ce qu'on a ajouté du nombre arabe
        arabe -= i*VALEURS_SYMBOLES[j];
    }

    //Simplifcation---------------------------------------------------

    for (int j=2; j<NOMBRES_SYMBOLES; j+=2)
    {
        //On crée une flux sur une chaine de caractère pour pouvoir écrire des nombres dedans.
        ostringstream flux;

        //On remplace les symboles repetes par les chaines raccourcies
        switch (romain.rfind(SYMBOLES[j]) - romain.find(SYMBOLES[j]) + 1)
        {
        case 9:
            flux << SYMBOLES[j] << SYMBOLES[j-2];
            romain.replace(romain.find(SYMBOLES[j]), 9, flux.str());
            break;
        case 8:
            flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 8, flux.str());
            break;
        case 7:
            flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 7, flux.str());
            break;
        case 6:
            flux << SYMBOLES[j-1] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 6, flux.str());
            break;
        case 5:
            flux << SYMBOLES[j-1];
            romain.replace(romain.find(SYMBOLES[j]), 5, flux.str());
            break;
        case 4:
            flux << SYMBOLES[j] << SYMBOLES[j-1];
            romain.replace(romain.find(SYMBOLES[j]), 4, flux.str());
            break;
        default:
            break;
        }
    }

    //On renvoit la chaine
    return romain;
}


L'avantage de cette fonction plus courte c'est qu'elle est aussi plus générique. Si les romains décident un jour d'ajouter plus de symboles à leurs nombres (On sait jamais hein !) et bien on aura pas besoin de toucher la fonction mais juste les tableaux définis avant.

...et le contraire

La fonction inverse n'est pas plus compliquée à écrire une fois que l'on a posé l'algorithme sur papier. La seule astuce et d'utiliser la fonction de conversion arabe->romain pour vérifier que le nombre romain que l'utilisateur a entré est bien la solution la plus courte.

int romanToArab(const string& roman)
{
    //On verifie que la chaine ne contient que des symboles autorises
    if (roman.find_first_not_of(SYMBOLES) != string::npos)
        throw string("Symbole incorrect dans le nombre (Règle 0)");

    int arabe = 0;
    int derniereValeur = 0;
    int plusGrandeValeur = 0;

    //On parcourt la chaine a l'envers
    for (int pos = roman.size()-1; pos>=0; --pos)
    {
        //On cherche la valeur du symbole actuel
        const int valeur = VALEURS_SYMBOLES[SYMBOLES.find(roman[pos])];

        //Si le symbole actuel est plus grand que celui a sa droite on additione le symbole
        if (valeur >= derniereValeur)
        {
            //Si le symbole est plus petit que un de ceux à sa droite, c'est une erreur
            if(valeur < plusGrandeValeur)
                throw string("Ordre des symboles incorrects (Règle 1)");

            //Sinon, c'est bon. On additionne donc
            arabe += valeur;
        }
        //Sinon on est dans le cas d'une "soustraction"
        else
        {
            //On verifie que la soustraction est valable
            if (derniereValeur != 5*valeur && derniereValeur != 10*valeur)
                throw string("Soustraction invalide (Règle 3)");

            //Si c'est bon on retranche
            arabe -= valeur;
        }

        derniereValeur = valeur;
        if(valeur > plusGrandeValeur)
            plusGrandeValeur = valeur;
    }

    //On verifie que le nombre que l'utilisateur a entre n'était en fait pas trop grand
    if (arabe < 1 || arabe > 4999)
        throw string("Le nombre doit être compris entre 1 et 4999 (Règle 4)");

    //On vérifie si c'était bien la version la plus courte
    if (roman != arabToRoman(arabe))
    {
        ostringstream flux;
        flux << "Le nombre doit être écrit dans sa forme la plus courte (Règle 5)\nLa version correcte est: " <<arabToRoman(arabe);
        flux << ". Ce qui donne " << arabe << " en chiffres arabes.";

        throw flux.str();
    }

    return arabe;
}


N'hésitez pas à demander des détails, si vous en avez besoin. Voici finalement le code source complet avec un main() pour tester.

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

const string SYMBOLES = "MDCLXVI";
const int NOMBRES_SYMBOLES = 7;
const int VALEURS_SYMBOLES[NOMBRES_SYMBOLES] = {1000, 500, 100, 50, 10, 5, 1};

string arabToRoman(int arabe)
{
    //On commence par tester si le nombre est valide
    if (arabe < 1 || arabe > 4999)
        throw string("Le nombre doit être compris entre 1 et 4999");

    //La chaine que l'on va renvoyer
    string romain;

    for (int j=0; j<NOMBRES_SYMBOLES; j+=2)
    {
        int i=0;

        //On compte le nombre de symboles d'un type donné à écrire
        for (i = 0; i<arabe/VALEURS_SYMBOLES[j]; ++i)
            romain += SYMBOLES[j];

        //On supprime ce qu'on a ajouté du nombre arabe
        arabe -= i*VALEURS_SYMBOLES[j];
    }

    //Simplifcation---------------------------------------------------

    for (int j=2; j<NOMBRES_SYMBOLES; j+=2)
    {
        //On crée une flux sur une chaine de caractère pour pouvoir écrire des nombres dedans.
        ostringstream flux;

        //On remplace les symboles repetes par les chaines raccourcies
        switch (romain.rfind(SYMBOLES[j]) - romain.find(SYMBOLES[j]) + 1)
        {
        case 9:
            flux << SYMBOLES[j] << SYMBOLES[j-2];
            romain.replace(romain.find(SYMBOLES[j]), 9, flux.str());
            break;
        case 8:
            flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 8, flux.str());
            break;
        case 7:
            flux << SYMBOLES[j-1] << SYMBOLES[j] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 7, flux.str());
            break;
        case 6:
            flux << SYMBOLES[j-1] << SYMBOLES[j];
            romain.replace(romain.find(SYMBOLES[j]), 6, flux.str());
            break;
        case 5:
            flux << SYMBOLES[j-1];
            romain.replace(romain.find(SYMBOLES[j]), 5, flux.str());
            break;
        case 4:
            flux << SYMBOLES[j] << SYMBOLES[j-1];
            romain.replace(romain.find(SYMBOLES[j]), 4, flux.str());
            break;
        default:
            break;
        }
    }

    //On renvoit la chaine
    return romain;
}

int romanToArab(const string& roman)
{
    //On verifie que la chaine ne contient que des symboles autorises
    if (roman.find_first_not_of(SYMBOLES) != string::npos)
        throw string("Symbole incorrect dans le nombre (Règle 0)");

    int arabe = 0;
    int derniereValeur = 0;
    int plusGrandeValeur = 0;

    //On parcourt la chaine a l'envers
    for (int pos = roman.size()-1; pos>=0; --pos)
    {
        //On cherche la valeur du symbole actuel
        const int valeur = VALEURS_SYMBOLES[SYMBOLES.find(roman[pos])];

        //Si le symbole actuel est plus grand que celui a sa droite on additione le symbole
        if (valeur >= derniereValeur)
        {
            //Si le symbole est plus petit que un de ceux à sa droite, c'est une erreur
            if(valeur < plusGrandeValeur)
                throw string("Ordre des symboles incorrects (Règle 1)");

            //Sinon, c'est bon. On additionne donc
            arabe += valeur;
        }
        //Sinon on est dans le cas d'une "soustraction"
        else
        {
            //On verifie que la soustraction est valable
            if (derniereValeur != 5*valeur && derniereValeur != 10*valeur)
                throw string("Soustraction invalide (Règle 3)");

            //Si c'est bon on retranche
            arabe -= valeur;
        }

        derniereValeur = valeur;
        if(valeur > plusGrandeValeur)
            plusGrandeValeur = valeur;
    }

    //On verifie que le nombre que l'utilisateur a entre n'était en fait pas trop grand
    if (arabe < 1 || arabe > 4999)
        throw string("Le nombre doit être compris entre 1 et 4999 (Règle 4)");

    //On vérifie si c'était bien la version la plus courte
    if (roman != arabToRoman(arabe))
    {
        ostringstream flux;
        flux << "Le nombre doit être écrit dans sa forme la plus courte (Règle 5)\nLa version correcte est: " <<arabToRoman(arabe);
        flux << ". Ce qui donne " << arabe << " en chiffres arabes.";

        throw flux.str();
    }

    return arabe;
}

int main()
{
    try
    {
        cout << "Conversion de 3593: " << flush;
        cout << arabToRoman(3593) << endl;
        cout << "Conversion de MMDCCM: " << flush;
        cout << romanToArab("MMDCCM") << endl;
    }
    catch (string& e)
    {
        cerr << "\nERREUR: " << e << endl;
    }
    return 0;
}


Posez des questions si vous avez besoin de plus de détails !

Pour aller plus loin....

(beaucoup plus loin)

Dans un post précédent, j'avais lancer une petite pique sans penser que quelqu'un prendrait cela au sérieux. C'était compter sans Hunlyxxod qui a relevé le défi.

Il a réalisé cet exercice entièrement en métaprogrammation. Bravo à lui.
Voici son code:

#include <iostream>

/*****************************************************************************/
/* Template utilitaire
/*****************************************************************************/

// Classe représentant un entier
template <int _Number>
struct Integer
{
	enum { Value = _Number };
};

// Classe représentant une liste
template <class _Data, class _Next>
struct List
{
	typedef _Data Data;
	typedef _Next Next;
};

// Converti une liste de caractères en chaîne de caractères
template <class _List>
struct String
{
	String(): m_data(_List::Data::Value) {};

	const char m_data;
	String<typename _List::Next> m_next;

private:
	String<_List> & operator =(const String<_List> &);
};

template <>
struct String<void>
{
	String(): m_data('\0') {};

	const char m_data;

private:
	String<void> & operator =(const String<void> &);
};

// Macros pour faciliter l'écriture de chaînes de caractères
#define EXPAND(args)			args
#define METASTRINGNULL			void
#define METASTRING1(c)			List<Integer<c>, METASTRINGNULL>
#define METASTRING2(c, ...)		List<Integer<c>, EXPAND(METASTRING1(__VA_ARGS__))>
#define METASTRING3(c, ...)		List<Integer<c>, EXPAND(METASTRING2(__VA_ARGS__))>
#define METASTRING4(c, ...)		List<Integer<c>, EXPAND(METASTRING3(__VA_ARGS__))>
#define METASTRING5(c, ...)		List<Integer<c>, EXPAND(METASTRING4(__VA_ARGS__))>
#define METASTRING6(c, ...)		List<Integer<c>, EXPAND(METASTRING5(__VA_ARGS__))>
#define METASTRING7(c, ...)		List<Integer<c>, EXPAND(METASTRING6(__VA_ARGS__))>
#define METASTRING8(c, ...)		List<Integer<c>, EXPAND(METASTRING7(__VA_ARGS__))>
#define METASTRING9(c, ...)		List<Integer<c>, EXPAND(METASTRING8(__VA_ARGS__))>
#define METASTRING10(c, ...)	List<Integer<c>, EXPAND(METASTRING9(__VA_ARGS__))>
#define METASTRING11(c, ...)	List<Integer<c>, EXPAND(METASTRING10(__VA_ARGS__))>
#define METASTRING12(c, ...)	List<Integer<c>, EXPAND(METASTRING11(__VA_ARGS__))>
#define METASTRING13(c, ...)	List<Integer<c>, EXPAND(METASTRING12(__VA_ARGS__))>
#define METASTRING14(c, ...)	List<Integer<c>, EXPAND(METASTRING13(__VA_ARGS__))>
#define METASTRING15(c, ...)	List<Integer<c>, EXPAND(METASTRING14(__VA_ARGS__))>
#define METASTRING16(c, ...)	List<Integer<c>, EXPAND(METASTRING15(__VA_ARGS__))>

/*****************************************************************************/
/* Définitions pour les chiffres romains
/*****************************************************************************/

// Structure contenant les informations d'un symbole romain
template <char _Letter, unsigned _Number, class _Subtract = void >
struct RomanSymbol
{
	typedef Integer<_Letter> Letter;
	typedef Integer<_Number> Number;
	typedef _Subtract Subtract;
};

// Symboles romains utilisés
typedef RomanSymbol<'I',    1>		I;
typedef RomanSymbol<'V',    5, I>	V;
typedef RomanSymbol<'X',   10, I>	X;
typedef RomanSymbol<'L',   50, X>	L;
typedef RomanSymbol<'C',  100, X>	C;
typedef RomanSymbol<'D',  500, C>	D;
typedef RomanSymbol<'M', 1000, C>	M;

// Liste contenant tous les symbols romains utilisés
typedef List<M, List<D, List<C, List<L, List<X, List<V, List<I, void> > > > > > > RomanSymbols;

/*****************************************************************************/
/* Conversion arabes -> romains
/*****************************************************************************/

// ConvertToRoman:
// Convertit _Number en _Symbols
template <int _Number, class _Symbols>
struct ConvertToRoman;

// ConvertToRomanSubtract:
// Essayes d'insérer une soustraction dans la conversion de _Number en _Symbols
template <int _Number, class _Symbols>
struct ConvertToRomanSubtract;

// ConvertToRomanSubtract_Test<true>
// Insérer une soustraction est possible
template <bool _Test, int _Number, class _Symbols>
struct ConvertToRomanSubtract_Test
{
	typedef
		List<
			typename _Symbols::Data::Subtract::Letter,
			List<
				typename _Symbols::Data::Letter,
				typename ConvertToRoman<
					_Number - _Symbols::Data::Number::Value + _Symbols::Data::Subtract::Number::Value,
					typename _Symbols::Next>::Result > >
		Result;
};

// ConvertToRomanSubtract_Test<false>
// Pas possible de placer une soustraction, converti le nombre avec les symboles suivants
template <int _Number, class _Symbols>
struct ConvertToRomanSubtract_Test<false, _Number, _Symbols>
{
	typedef typename ConvertToRoman<_Number, typename _Symbols::Next>::Result Result;
};

template <int _Number, class _Symbols>
struct ConvertToRomanSubtract
{
	typedef 
		typename ConvertToRomanSubtract_Test<
			(_Number >= _Symbols::Data::Number::Value - _Symbols::Data::Subtract::Number::Value),
			_Number,
			_Symbols>::Result
		Result;
};

// ConvertToRoman_Test<true>
// Le premier symbole est insérable en début de la chaîne de caractères,
// on l'insère et on converti le reste du nombre
template <bool _Test, int _Number, class _Symbols>
struct ConvertToRoman_Test
{
	typedef
		List<
			typename _Symbols::Data::Letter,
			typename ConvertToRoman<_Number - _Symbols::Data::Number::Value, _Symbols>::Result
		> Result;
};

// ConvertToRoman_Test<false>
// Le premier symbole n'est pas insérable,
// on essaye avec une soustraction
template <int _Number, class _Symbols>
struct ConvertToRoman_Test<false, _Number, _Symbols>
{
	typedef
		typename ConvertToRomanSubtract<_Number, _Symbols>::Result
		Result;
};

template <int _Number, class _Symbols>
struct ConvertToRoman
{
	typedef
		typename ConvertToRoman_Test<
			(_Number >= _Symbols::Data::Number::Value),
			_Number,
			_Symbols >::Result
		Result;
};

// Condition de fin:
// Le nombre est 0, il n'y a plus rien à convertir
template <class _Symbols>
struct ConvertToRoman<0, _Symbols>
{
	typedef void Result;
};

/*****************************************************************************/
/* Conversion romains -> arabes
/*****************************************************************************/

template <char _Symbol>
struct SymbolNotValid;

// ConvertToArabic:
// Convertit la liste de symboles _String en nombre
template <class _String, class _Symbols>
struct ConvertToArabic;

// ConvertToArabicSubtract:
// Teste si les premiers symboles de _String sont une soustraction
template <class _String, class _Symbols>
struct ConvertToArabicSubtract;

// ConvertToArabicSubtract_Test<true>
// Les deux premiers symboles sont une soustraction,
// on converti les symboles suivants et on l'ajoute à la soustraction
template <bool _Test, class _String, class _Symbols>
struct ConvertToArabicSubtract_Test
{
	enum {
		Value = _Symbols::Data::Number::Value - _Symbols::Data::Subtract::Number::Value +
		ConvertToArabic<typename _String::Next::Next, typename _Symbols::Next>::Value
	};
};

// ConvertToArabicSubtract_Test<false>
// Les deux premiers symboles ne sont pas une soustraction
template <class _String, class _Symbols>
struct ConvertToArabicSubtract_Test<false, _String, _Symbols>
{
	enum {
		Value = ConvertToArabic<_String, typename _Symbols::Next>::Value
	};
};

template <class _String, class _Symbols>
struct ConvertToArabicSubtract
{
	enum {
		Value = ConvertToArabicSubtract_Test<
			static_cast<int>(_String::Data::Value) == static_cast<int>(_Symbols::Data::Subtract::Letter::Value) &&
			static_cast<int>(_String::Next::Data::Value) == static_cast<int>(_Symbols::Data::Letter::Value),
			_String,
			_Symbols>::Value
	};
};

// La liste ne contient qu'un symbole, ça ne peut être une soustraction
template <class _Char, class _Symbols>
struct ConvertToArabicSubtract<List<_Char, void>, _Symbols>
{
	enum {
		Value = ConvertToArabic<List<_Char, void>, typename _Symbols::Next>::Value
	};
};

// Le symbole à tester n'accepte pas de soustraction, on passe le test
template <class _String, char _Letter, unsigned _Number, class _Next>
struct ConvertToArabicSubtract<_String, List<RomanSymbol<_Letter, _Number, void>, _Next> >
{
	enum {
		Value = ConvertToArabic<_String, _Next>::Value
	};
};

// Si on a passé tous les symboles sans succès, on signale une erreur
template <class _String>
struct ConvertToArabicSubtract<_String, void>
{
	enum { Error = sizeof(SymbolNotValid<_String::Data::Value>) };
};

// ConvertToArabic_Test<true>
// On a trouvé le premier symbole de _String, on fait la conversion,
// et on passe au suivant
template <bool _Test, class _String, class _Symbols>
struct ConvertToArabic_Test
{
	enum {
		Value = _Symbols::Data::Number::Value + ConvertToArabic<typename _String::Next, _Symbols>::Value
	};
};

// ConvertToArabic_Test<false>
// Le premier symbole de _String n'est pas le premier symbole de _Symbols,
// on vérifie que ce ne soit pas une soustraction
template <class _String, class _Symbols>
struct ConvertToArabic_Test<false, _String, _Symbols>
{
	enum {
		Value = ConvertToArabicSubtract<_String, _Symbols>::Value
	};
};

template <class _String, class _Symbols>
struct ConvertToArabic
{
	enum {
		Value = ConvertToArabic_Test<static_cast<int>(_String::Data::Value) == static_cast<int>(_Symbols::Data::Letter::Value), _String, _Symbols>::Value
	};
};

// On a parcouru toutes la liste de symbole avec succès
template <class _Symbols>
struct ConvertToArabic<void, _Symbols>
{
	enum { Value = 0 };
};

// Un symbole de la liste n'a pas pu être trouvé, on signale une erreur
template <class _String>
struct ConvertToArabic<_String, void>
{
	enum { Error = sizeof(SymbolNotValid<_String::Data::Value>) };
};

/*****************************************************************************/
/* Programme de test
/*****************************************************************************/

#define ARABIC_NUMBER 2938
#define ROMAN_NUMBER METASTRING8('M', 'C', 'M', 'L', 'X', 'I', 'X', 'V')

String<ConvertToRoman<ARABIC_NUMBER, RomanSymbols>::Result> ArabicString;
String<ROMAN_NUMBER> RomanString;

int main()
{
	std::cout << ARABIC_NUMBER << " equivaut a " << &ArabicString.m_data << std::endl;
	std::cout << &RomanString.m_data << " equivaut a " << ConvertToArabic<ROMAN_NUMBER, RomanSymbols>::Value << std::endl;

	return 0;
}


Voilà. Amusez-vous bien avec l'exercice sur les polynômes. Les corections manquantes arrivent tout bientôt...
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
31 mars 2009 à 17:53:03

En première, on en sait déjà assez pour savoir faire tout l'exercice : multiplication, addition..., factorisation, unicité d'écriture d'un polynôme... Certes pas poussé ni formellement démontré et énoncé...
Les binômes, les trinômes, et parfois les polynômes de degré 3 sont étudiés (quadrinômes ?), surtout dans l'optique de résolution d'équations.
Cependant, le discours formaté de soutenir qu'on n'apprend plus rien à l'école de nos jours quand on l'a entendu dire, qu'on ne la connait que par certains répétent n'est pas entiérement vrai.
31 mars 2009 à 18:03:52

Mmmh je crois qu'on a compris.

Je pense pas que ce soit le lieu pour un comparatif entre établissements scolaires ni pour un débat sur l'éducation...
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.