Partage

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

5 novembre 2008 à 14:15:27

Eh bien, si tu veux que ta solution puisse servir de correction pour les autres, oui.

Si tu veux juste t'entrainer sur certains points, alors libre à toi de faire ce que tu veux.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
11 novembre 2008 à 18:43:40

Bonjour,

Ok, mais une question se pose, comment faire pour compter le nombre de chiffre qu'un nombre contient (en C++) ? o_O
Ex :

Citation : Moi

j'envoie 12.41110 a la fonction et elle me renvoie 7


Car elle sera très pratique pour ma classe !

EDIT : sais autre chose que je voulait marquer
11 novembre 2008 à 20:42:34

Si tu as un problème, poste dans un nouveau topic. Je serais ravi de t'aider ;) .
14 novembre 2008 à 20:40:18

Ca y est ! J'ai envoyé mon code :) . Je n'ai malheureusement pas réussi à tout implanter... :(
15 novembre 2008 à 2:22:46

Depuis trois jours je travaille sur ce projet, j'ai presque terminé de tout implémenté. Seulement le plus répétitif reste à faire, sinon j'ai terminé!
15 novembre 2008 à 2:28:04

Citation : Thunder91

Ca y est ! J'ai envoyé mon code :) . Je n'ai malheureusement pas réussi à tout implanter... :(



Donc ça sert pas à grnad chose de l'envoyer. Puisqu'il ne pourra pas servir de correction...
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
Anonyme
15 novembre 2008 à 17:44:47

A ce propos, quand est-ce qu'on aura cette correction ? Le délai est dépassé, non ?
16 novembre 2008 à 13:54:43

Quelques personnes ont demandé un peu plus de temps. Mais dans 1 ou 2 jours ce sera là.

En attendant, vous pouvez toujours jetter un oeil sur une version professionelle de cette classe que quelques membres avaient suggérés :

http://www.boost.org/doc/libs/1_37_0/boost/rational.hpp

Attention, c'est du lourd et ne correspond pas tout à fait à l'énoncé mais une grande partie y est.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
19 novembre 2008 à 17:53:34

Exercice du mois de novembre 2008



Nom : CRYPTO 1: Chiffre de César
Sujet : chaînes de caractères, algo


Introduction générale



L'utilisation du ROT13 dans certains des posts précédents, m'a donné envie de débuter une série d'exercices sur la cryptologie. Nous allons explorer quelques méthodes de cryptage de données dans les exercices qui vont suivre par ordre croissant de difficulté.

Pour chaque algorithme de cryptage, il y aura plusieurs parties afin de représenter les différentes disciplines de la cryptologie.

1) La partie cryptographie qui consiste à chiffrer et déchiffrer un message avec l'aide ou non d'une clé.

2) La partie cryptanalyse qui consiste à "casser" le code et donc à découvrir un message secret sans connaître la clé.

La partie 2 étant plus difficile que la partie 1.

Introduction de l'exercice du mois



Le premier algorithme que nous allons étudier, est le chiffre de César ou si on prend le cas général, le chiffrement par décalage.

L'algorithme consiste à décaler les lettres d'un message d'un certain nombre de position dans l'alphabet. Par exemple pour un décalage de 1, le texte:

SITE DU ZERO

devient:

TJUF EV AFSP

Le décryptage se fait de la même manière en utilisant le décalage opposé.

On ne considère que des messages sans accents.



Votre programme



Niveau 1



Vous devez fournir un programme permettant de crypter un message en laissant la liberté à l'utilisateur de choisir le décalage qu'il désire utiliser.
Ce programme devra aussi permettre de décrypter les message reçus.

Vous pouvez aussi donner la possibilité de crypter (et décrypter) entièrement un fichier.

Niveau 2



Votre but cette fois, est de "casser" le code sans connaître le décalage qui a été utilisé.

Si vous avez besoin de conseils:
http://fr.wikipedia.org/wiki/Analyse_fréquentielle


Vous avez jusqu'au 15 décembre 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.
19 novembre 2008 à 19:33:43

Un petit truc qui me tracasse :

Citation : Nanoc

1) La partie cryptanalyse qui consiste à chiffrer et déchiffrer un message avec l'aide ou non d'une clé.

2) La partie cryptologie qui consiste à "casser" le code et donc à découvrir un message secret sans connaître la clé.


Ce serait pas le contraire : la cryptologie consiste à chiffrer/déchiffrer et la cryptanalyse à casser un cryptage ?
Sinon c'est une bonne idée de sujet(s), je compte participer :) .
19 novembre 2008 à 19:50:43

En effet. Merci bien.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
19 novembre 2008 à 20:17:40

Pour la cryptanalyse, on peut considérer que les 3 lettres les plus utilisées sont le e, le a et le i et ensuite les déterminer en utilisant le décalage entre les 3 caractères obtenus ?
20 novembre 2008 à 0:43:00

C'est exactement le principe au quel je pensais. Sur un long texte (Au moins une page), cela fonctionne pratiquement toujours.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
21 novembre 2008 à 12:57:36

Solution du mois d'octobre



Bonjour tout le monde ! Il est temps que je dévoile une solution pour l'exercice du mois d'octobre. Vous avez été 22 à m'envoyer une solution.
Il y avait beaucoup de bonnes choses dans les codes reçus. Cependant, je n'ai vu personne qui avait implémenté les opérateurs et les constructeurs correctement. C'était toujours soit l'un soit l'autre. Voici donc ma solution.

La base de la classe



Nous devons réaliser une classe représentant la notion de fraction. Elle aura donc naturellement deux attributs, deux nombres entiers qui représenteront le numérateur et le dénominateur de la fraction.
Et là on peut se poser la première question : "Public ou privé ?" Habituellement, les attributs d'une classe devraient être privés, mais dans notre cas, nous voulons donner l'accès aux attributs de la classe à l'utilisateur.
On a alors deux possibilités:
1) Mettre des fonctions "get()" et "set()" pour nos attributs.
2) Mettre les attributs en public.

La solution 1 permet d'avoir un contrôle sur ce que l'utilisateur fait (par exemple empêcher un dénominateur nul), mais est légèrement plus lourd à programmer. La solution 2 donne un accès sans contrôle à l'utilisateur, mais on peut supposer qu'un utilisateur sensé saura que le dénominateur d'une fonction ne doit pas être nul.

J'ai choisi ici la solution 1, afin de vous montrer un cas concret d'utilisation des exceptions, mais la 2 me semble tout à fait applicable dans ce contexte. A ce stade, nous avons donc:

class Fraction
{
private:
   int numerateur;
   int denominateur;
};


Pour ce qui est du signe, j'ai décidé d'utiliser la convention que le dénominateur est toujours positif et que seul le numérateur porte le signe.

Les constructeurs simples



Pour notre classe, il nous faut un ou plusieurs constructeurs. J'avais demandé l'écriture d'un constructeur à partir d'un entier et d'un constructeur à partir d'un couple d'entier.
Si l'on se souvient que <math>\(3 = \frac{3}{1}\)</math>, on peut gagner du temps en écrivant qu'un seul constructeur avec un second argument possédant une valeur par défaut. On peut même aller plus loin et proposer un constructeur entièrement par défaut en mettant une valeur pour le premier argument. Ainsi on a un constructeur par défaut qui crée une fraction valant zéro.

Fraction(int numerateur=0,int denominateur=1)


Dans le corps, il faut faire trois vérifications.
1) Vérifier que le dénominateur est non-nul.
2) Simplifier la fraction si possible.
3) Gérer le signe si l'utilisateur essaye de faire une fraction <math>\(\frac{5}{-3}\)</math> par exemple.

Pour les points 2, nous allons créer des fonctions dédiées puisque les simplifications seront aussi utiles pour les opérateurs. Nous avons donc le code suivant dans notre ".cpp" :

#include <stdexcept> //Pour les exceptions

Fraction::Fraction(int n,int d)
        :numerateur(std::abs(n)), //On stocke la valeur absolue puisque on veut gerer nous-meme le signe
        denominateur(std::abs(d))
{
    //Le denominateur nul est interdit on lance une exception si ca arrive
    if (d==0) throw std::runtime_error("Fraction: Denominateur nul");

    //Si il y a un signe, il va au numerateur
    if (n*d < 0)
        numerateur = -numerateur;

    //Et on simplifie la fraction si possible
    simplifie();
}


std::runtime_error est un type d'exception défini dans la bibliothèque standard que l'on utilise pour spécifier les erreurs se produidant lors de l'exécution.

Et le constructeur de copie ?

On en a pas besoin vu que notre classe ne contient pas de pointeur et qu'il n'y a aucune opération "spéciale" à réaliser lors d'une copie. Le constructeur de copie généré par le compilateur sera suffisant.

Les autres constructeurs seront détaillés plus loin.

Simplifier une fraction



Une fraction est simplifiable si son numérateur et son dénominateur ont un pgdc (plus grand diviseur commun) différent de 1. Il nous faut donc une fonction qui calcule le pgdc. Cette fonction n'est pas spécifique à la classe, elle n'a donc rien à faire dans la classe; elle "vit" à côté. On utilise simplement l'algorithme usuel.

int pgcd(int a,int b)
{
    while (b!=0) 
    {
        const int temp = b;
        b = a % b;   //On cherche le reste de la division de a par b tant que b est different de 0
        a = temp;
    }
    return a;
}


On peut maintenant mettre notre fonction de simplication dans notre classe. Cette fonction va dans la partie privée, puisque l'utilisateur n'a pas à l'appeler directement.

void Fraction::simplifie()
{
    //Calcul du pgdc du numerateur et du denominateur
    const int diviseur = pgcd(std::abs(numerateur),denominateur);

    //Si on peut simplifier la fraction, alors on simplifie
    if (diviseur != 1)
    {
        numerateur /= diviseur;
        denominateur /= diviseur;
    }
}


Pour ce qui est de la gestion du signe, on a également une fonction privée qui va mettre le signe au numérateur si nécessaire.

void Fraction::signe()
{
    if (numerateur * denominateur >= 0)  //Si le numerateur et le denominateur sont positifs ou négatifs
    {                                    //Alors la fraction est positive
        numerateur = std::abs(numerateur);
        denominateur = std::abs(denominateur);
    }
    else
    {                                   //Sinon, elle est negative
        numerateur = -std::abs(numerateur);
        denominateur = std::abs(denominateur);
    }
}


On ne peut pas utiliser cette fonction dans le constructeur puisque, on stocke les valeurs absolues dans un premier temps.


Les accesseurs



Il nous faut deux fonctions permettant d'accéder au numérateur et au dénominateur. Ces fonctions sont spécifiées "const" puisqu'elles ne modifient pas la fraction.

int getNumerateur() const;
int getDenominateur() const;


L'opérateur d'affichage



La signature de l'opérateur d'affichage est toujours:
std::ostream& operator<<(std::ostream& flux,const Fraction& f);


Cette fonction est toujours en-dehors de la classe.


L'affichage doit se faire sout la forme "numerateur" / "denominateur" sauf si le denominateur vaut 1, auquel cas on affiche que le numérateur. Cela nous donne donc:

std::ostream& operator<<(std::ostream& flux,const Fraction& f)
{
    //On affiche le numerateur
    flux << f.getNumerateur();

    //Et le numerateur si il ne vaut pas 1
    if (f.getDenominateur() != 1)
        flux << "/" << f.getDenominateur();

    return flux;
}


Fonction toDouble()



La fonction permettant de convertir une fraction en un nombre à virgule est très simple, puisqu'il suffit de diviser le numérateur par le dénominateur pour obtenir le nombre souhaité.

double Fraction::toDouble() const
{
    //Division du numerateur par le denominateur
    return static_cast<double>(numerateur)/denominateur;
}


Remarquez à nouveau le "const" et le fait qu'on utilise "static_cast<double>" en C++ pour transformer un nombre entier en nombre à virgule.

Opérateurs de comparaison



Où mettre ces opérateurs ? Dans la classe ou en-dehors ? Les deux sont possibles, mais mettre les opérateurs prenant deux opérandes en-dehors de la classe est souvent (j'aurais aimé mettre toujours :) ) mieux. En effet, vu que nous avons des constructeurs int -> Fraction et double -> Fraction, notre opérateur déclaré à l'extérieur permettera également de faire des tests du type <math>\(2.5 == \frac{5}{2}\)</math> qui ne seraient pas possible si l'on avait défini l'opérateur à l'intérieur de la classe. En effet, dans ce cas l'opérande de gauche doit toujours être l'objet, ce qui n'est pas le cas dans l'exemple que je vous ai fourni.

Nous avons donc en-dehors de la classe:
bool operator==(const Fraction& f,const Fraction& g);
bool operator!=(const Fraction& f,const Fraction& g);
bool operator<=(const Fraction& f,const Fraction& g);
bool operator>=(const Fraction& f,const Fraction& g);
bool operator<(const Fraction& f,const Fraction& g);
bool operator>(const Fraction& f,const Fraction& g);


Remarquez les passages par référence constante pour éviter la copie inutile de la Fraction.

Pour tester l'égalité, il suffit de vérifier que le numérateur et le dénominateur de la fraction sont identiques. Ce qui nous donne:
bool operator==(const Fraction& f,const Fraction& g)
{
    return f.getNumerateur() == g.getNumerateur() && f.getDenominateur() == g.getDenominateur();
}


et donc pour tester la différence:
bool operator!=(const Fraction& f,const Fraction& g)
{
    return !(f == g);
}


Pour les opérateurs de comparaisons, on peut se simplifier la vie en convertissant nos fractions en double pour la comparaison. Ce qui donne:

bool operator<=(const Fraction& f,const Fraction& g)
{
    return f.toDouble() <= g.toDouble();
}


Et pour l'opérateur opposé:
bool operator>(const Fraction& f,const Fraction& g)
{
    return !(f<=g);
}


Opérateurs mathématiques



La bonne manière de réaliser cela est de mettre les opérateurs +=,-=,... dans la classe et les opérateurs +,-,... en-dehors pour les mêmes raisons que précédemment. On a donc le code suivant pour l'opérateur += :

Fraction& Fraction::operator+=(const Fraction& f)
{
    //On calcule la somme
    numerateur = numerateur * f.denominateur + f.numerateur * denominateur;
    denominateur *= f.denominateur;

    //Et on simplifie
    simplifie();

    return *this;
}


Vous remarquerez le type de retour ainsi que le fait que l'on passe l'argument par référence constante.
Avec cela, on peut écrire l'opérateur +, ce qui donne :

Fraction operator+(const Fraction& f,const Fraction& g)
{
    return Fraction(f)+=g;
}


Les autres opérateurs fonctionnent de la même manière à partir des règles que vous avez appris à l'école.

Pour le ^=, on va juste multiplier la fraction par elle-même un certain nombre de fois.

Fraction& Fraction::operator^=(int n)
{
    //Les valeurs a multiplier
    const int num = numerateur;
    const int den = denominateur;

    numerateur = 1;
    denominateur = 1;
    //On multiplie par la fraction n fois
    for (int i(0);i < std::abs(n); ++i)
    {
        numerateur *= num;
        denominateur *= den;
    }

    //Si la puissance etait negative, on inverse la fraction
    if (n < 0)
        std::swap(numerateur,denominateur);

    //On gere le signe
    signe();

    //On simplifie la fraction
    simplifie();

    return *this;
}


Autres opérateurs



On peut également écrire d'autres opérateurs comme par exemple le moins unitaires (celui qui permet d'écrire -a). C'est également une fonction externe à la classe:

Fraction operator-(const Fraction& f)
{
    return Fraction(-f.getNumerateur(),f.getDenominateur());
}


On peut également écrire les opérateurs ++ et -- :
Fraction& Fraction::operator++()
{
    return *this += 1;
}


Construction depuis une string



Une bonne solution consiste à utiliser les stringstream pour extraire les nombres depuis la chaine de caractère.

Fraction::Fraction(const std::string& x)
        :numerateur(0),
        denominateur(1)
{
    //On declare un flux depuis une chaine pour lire les chiffres
    std::istringstream flux(x);
    char symbole;  //Pour la lecture du /

    //On lit le numerateur
    flux >> numerateur;
    //On lit le /
    flux >> symbole;
    //On lit le denominateur
    flux >> denominateur;

    //Le denominateur nul est interdit on lance une exception si ca arrive
    if (denominateur==0) throw std::runtime_error("Fraction: Denominateur nul");

    //On gere le cas special du signe
    signe();

    //Et on simplifie la fraction si possible
    simplifie();
}


Cette version n'est pas sécurisée et suppose que l'utilisateur entre correctement les informations.


Construction depuis un double



Voici la version de Gymnopaul de ce constructeur. C'est la meilleure version que j'ai reçue et elle est plus simple que la mienne :

#include <cmath>

Fraction::Fraction(double nombre)
    :numerateur(0),
    denominateur(1)
{
    // On crée une chaîne pour le nombre
    std::string chaine;
    std::ostringstream oss;
    oss << nombre;
    chaine = oss.str();

    //On cherche la virgule et le nombre de ceimales
    bool pointTrouve = false;
    int nombredeDecimaux = 0;

    // On parcourt la chaîne
    for(size_t i=0; i<chaine.size(); i++)
    {
        // Si le point a été trouvé, le caractère chaine[i] est compris dans la partie décimale
        if(pointTrouve)
            nombredeDecimaux++;

        /* Si le caractère chaine[i] n'est pas un nombre, c'est le point
        et alors on peut compter les décimales */
        if(chaine[i] == '.')
            pointTrouve = true;
    }

    /* Grâce aux nombres de la partie décimale, on
    multiplie le nombre décimal en paramètre
    par une puissance de 10 du nombre de décimaux
    Cela donne le numérateur, et le dénominateur
    est la puissance de 10

    ex : pour le nombre 2.267 en paramètre,
    il y a 3 décimaux : 2, 6 et 7.
    On multiplie donc par 10^3 soit 1000,
    ce qui donne comme numérateur 2267
    et comme dénominateur 1000 */
    numerateur = static_cast<int>(nombre * std::pow(10., nombredeDecimaux));
    denominateur = static_cast<int>(std::pow(10., nombredeDecimaux));

    //On gere le signe
    signe();

    //et on simplifie
    simplifie();
}


Code complet



Voici un le code complet de la classe avec quelques trucs en plus.
Fraction.hpp:
#ifndef FRACTION_HPP
#define FRACTION_HPP

#include <string>
#include <ostream>

class Fraction
{
public:

    //Constructeurs
    Fraction(int n=0,int d=1);
    Fraction(double x);
    Fraction(const std::string& x);

    //Accesseurs
    int getNumerateur() const;
    int getDenominateur() const;

    //Operateurs mathematiques
    Fraction& operator+=(const Fraction& f);
    Fraction& operator-=(const Fraction& f);
    Fraction& operator*=(const Fraction& f);
    Fraction& operator/=(const Fraction& f);
    Fraction& operator^=(int n);

    //Incrementation
    Fraction& operator++();
    Fraction& operator--();
    Fraction operator++(int);  //Voir FAQ de developpez.com pour la difference
    Fraction operator--(int);

    //Fonctions membres
    double toDouble() const;

private:

    //Attributs
    int numerateur;
    int denominateur;

    //Fonctions utilitaires
    void simplifie();           //Simplifie la fraction si possible
    void signe();               //Met le signe uniquement au numerateur
};

//Operateurs de comparaisons
bool operator==(const Fraction& f,const Fraction& g);
bool operator!=(const Fraction& f,const Fraction& g);
bool operator<=(const Fraction& f,const Fraction& g);
bool operator>=(const Fraction& f,const Fraction& g);
bool operator<(const Fraction& f,const Fraction& g);
bool operator>(const Fraction& f,const Fraction& g);

//Test de nullite
bool operator!(const Fraction& f);      //Renvoit true si la fraction est nulle

//Operateurs mathématiques unitaires
Fraction operator-(const Fraction& f);
Fraction operator+(const Fraction& f);

//Operateurs mathématiques binaires
Fraction operator+(const Fraction& f,const Fraction& g);
Fraction operator-(const Fraction& f,const Fraction& g);
Fraction operator*(const Fraction& f,const Fraction& g);
Fraction operator/(const Fraction& f,const Fraction& g);
Fraction operator^(const Fraction& f,int n);

//Affichage dans un flux
std::ostream& operator<<(std::ostream&,const Fraction&);

//Valeur absolue
Fraction abs(const Fraction& f);

//Fonction mathématique utile
int pgcd(int a,int b);

#endif  //FRACTION_HPP


Fraction.cpp:
#include "Fraction.hpp"

#include <stdexcept>
#include <sstream>
#include <cmath>

// FONCTIONS MATHEMATIQUE UTILE  ------------------------------------------------------------

int pgcd(int a,int b)
{
    while (b!=0)
    {
        const int temp = b;
        b = a % b;   //On cherche le reste de la division de a par b tant que b est different de 0
        a = temp;
    }
    return a;
}

// CONSTRUCTEURS  ----------------------------------------------------------------------------

Fraction::Fraction(int n,int d)
        :numerateur(std::abs(n)),
        denominateur(std::abs(d))
{
    //Le denominateur nul est interdit on lance une exception si ca arrive
    if (d==0) throw std::runtime_error("Fraction: Denominateur nul");

    //Si il y a un signe, il va au numerateur
    if (n*d < 0)
        numerateur = -numerateur;

    //Et on simplifie la fraction si possible
    simplifie();
}

Fraction::Fraction(const std::string& x)
        :numerateur(0),
        denominateur(1)
{
    //On declare un flux depuis une chaine pour lire les chiffres
    std::istringstream flux(x);
    char symbole;  //Pour la lecture du /

    //On lit le numerateur
    flux >> numerateur;
    //On lit le /
    flux >> symbole;
    //On lit le denominateur
    flux >> denominateur;

    //Le denominateur nul est interdit on lance une exception si ca arrive
    if (denominateur==0) throw std::runtime_error("Fraction: Denominateur nul");

    //On gere le cas special du signe
    signe();

    //Et on simplifie la fraction si possible
    simplifie();
}

Fraction::Fraction(double nombre)
    :numerateur(0),
    denominateur(1)
{
    // On crée une chaîne pour le nombre
    std::string chaine;
    std::ostringstream oss;
    oss << nombre;
    chaine = oss.str();

    //On cherche la virgule et le nombre de ceimales
    bool pointTrouve = false;
    int nombredeDecimaux = 0;

    // On parcourt la chaîne
    for(size_t i=0; i<chaine.size(); i++)
    {
        // Si le point a été trouvé, le caractère chaine[i] est compris dans la partie décimale
        if(pointTrouve)
            nombredeDecimaux++;

        /* Si le caractère chaine[i] n'est pas un nombre, c'est le point
        et alors on peut compter les décimales */
        if(chaine[i] == '.')
            pointTrouve = true;
    }

    /* Grâce aux nombres de la partie décimale, on
    multiplie le nombre décimal en paramètre
    par une puissance de 10 du nombre de décimaux
    Cela donne le numérateur, et le dénominateur
    est la puissance de 10

    ex : pour le nombre 2.267 en paramètre,
    il y a 3 décimaux : 2, 6 et 7.
    On multiplie donc par 10^3 soit 1000,
    ce qui donne comme numérateur 2267
    et comme dénominateur 1000 */
    numerateur = static_cast<int>(nombre * std::pow(10., nombredeDecimaux));
    denominateur = static_cast<int>(std::pow(10., nombredeDecimaux));

    //On gere le signe
    signe();

    //et on simplifie
    simplifie();
}

//ACCESSEURS -----------------------------------------------------------------------------------

int Fraction::getNumerateur() const
{
    return numerateur;
}

int Fraction::getDenominateur() const
{
    return denominateur;
}

// FONCTIONS UTILITAIRES -----------------------------------------------------------------------

void Fraction::simplifie()
{
    //Calcul du pgdc du numerateur et du denominateur
    const int diviseur = pgcd(std::abs(numerateur),denominateur);

    //Si on peut simplifier la fraction, alors on simplifie
    if (diviseur != 1)
    {
        numerateur /= diviseur;
        denominateur /= diviseur;
    }

}

void Fraction::signe()
{
    if (numerateur * denominateur >= 0)  //Si le numerateur et le denominateur sont positifs ou négatifs
    {                                    //Alors la fraction est positive
        numerateur = std::abs(numerateur);
        denominateur = std::abs(denominateur);
    }
    else
    {                                   //Sinon, elle est negative
        numerateur = -std::abs(numerateur);
        denominateur = std::abs(denominateur);
    }
}

// OPERATEURS MATHEMATIQUES -------------------------------------------------------------------

Fraction& Fraction::operator+=(const Fraction& f)
{
    //On calcule la somme
    numerateur = numerateur * f.denominateur + f.numerateur * denominateur;
    denominateur *= f.denominateur;

    //Et on simplifie
    simplifie();

    return *this;
}

Fraction& Fraction::operator-=(const Fraction& f)
{
    //On calcule la difference
    numerateur = numerateur * f.denominateur - f.numerateur * denominateur;
    denominateur *= f.denominateur;

    //Et on simplifie
    simplifie();

    return *this;
}

Fraction& Fraction::operator*=(const Fraction& f)
{
    //On calcule le produit
    numerateur *= f.numerateur;
    denominateur *= f.denominateur;

    //Et on simplifie
    simplifie();

    return *this;
}

Fraction& Fraction::operator/=(const Fraction& f)
{
    //On calcule le produit
    numerateur *= f.denominateur;
    denominateur *= f.numerateur;

    //On gere le cas special du signe
    signe();

    //Et on simplifie
    simplifie();

    return *this;
}

Fraction& Fraction::operator^=(int n)
{

    const int num = numerateur;
    const int den = denominateur;

    numerateur = 1;
    denominateur = 1;
    //Cas general, on multiplie par la fraction n fois
    for (int i(0);i < std::abs(n); ++i)
    {
        numerateur *= num;
        denominateur *= den;
    }

    //Si la puissance etait negative, on inverse la fraction
    if (n < 0)
        std::swap(numerateur,denominateur);

    //On gere le signe
    signe();

    //On simplifie la fraction
    simplifie();

    return *this;
}

// FONCTION MEMBRE ------------------------------------------------------------------------------

Fraction& Fraction::operator++()
{
    return *this += 1;
}

Fraction& Fraction::operator--()
{
    return *this -= 1;
}

Fraction Fraction::operator++(int)
{
    const Fraction temp(*this);
    *this += 1;
    return temp;
}

Fraction Fraction::operator--(int)
{
    const Fraction temp(*this);
    *this -= 1;
    return temp;
}

// FONCTION MEMBRE ------------------------------------------------------------------------------

double Fraction::toDouble() const
{
    //Division du numerateur par le denominateur
    return static_cast<double>(numerateur)/denominateur;
}

// AFFICHAGE DANS UN FLUX -----------------------------------------------------------------------

std::ostream& operator<<(std::ostream& flux,const Fraction& f)
{
    //On affiche le numerateur
    flux << f.getNumerateur();

    //Et le numerateur si il ne vaut pas 1
    if (f.getDenominateur() != 1)
        flux << "/" << f.getDenominateur();

    return flux;
}

// OPERATEURS DE COMPARAISON -------------------------------------------------------------------

bool operator==(const Fraction& f,const Fraction& g)
{
    return f.getNumerateur() == g.getNumerateur() && f.getDenominateur() == g.getDenominateur();
}

bool operator!=(const Fraction& f,const Fraction& g)
{
    return !(f == g);
}

bool operator<=(const Fraction& f,const Fraction& g)
{
    return f.toDouble() <= g.toDouble();
}

bool operator>=(const Fraction& f,const Fraction& g)
{
    return g.toDouble() <= f.toDouble();
}

bool operator<(const Fraction& f,const Fraction& g)
{
    return !(f>=g);
}

bool operator>(const Fraction& f,const Fraction& g)
{
    return !(f<=g);
}

// TEST DENULLITE ------------------------------------------------------------------------------

bool operator!(const Fraction& f)
{
    return f.getNumerateur() == 0;
}

// OPERATEURS MATHEMATIQUES UNITAIRES ----------------------------------------------------------

Fraction operator-(const Fraction& f)
{
    return Fraction(-f.getNumerateur(),f.getDenominateur());
}

Fraction operator+(const Fraction& f)
{
    return Fraction(+f.getNumerateur(),f.getDenominateur());
}

// OPERATEURS MATHEMATIQUES BINAIRES -----------------------------------------------------------

Fraction operator+(const Fraction& f,const Fraction& g)
{
    return Fraction(f)+=g;
}

Fraction operator-(const Fraction& f,const Fraction& g)
{
    return Fraction(f)-=g;
}

Fraction operator*(const Fraction& f,const Fraction& g)
{
    return Fraction(f)*=g;
}

Fraction operator/(const Fraction& f,const Fraction& g)
{
    return Fraction(f)/=g;
}

Fraction operator^(const Fraction& f,int n)
{
    return Fraction(f)^=n;
}

// VALEUR ABSOLUE -------------------------------------------------------------------------------

Fraction abs(const Fraction& f)
{
    return Fraction(std::abs(f.getNumerateur()),f.getDenominateur());
}


-----------------------------------------------------------------------------------------------------

Si vous avez des questions, n'hésitez pas à les poser, je donnerai plus de détails. Une version "professionelle" et optimisée de cette classe se trouve dans boost.

A bientôt et bonne chance avec le nouvel exercice.

Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
21 novembre 2008 à 19:01:30

Bonjour,

Je vais participer a l'exercice de cryptage !
Et, très bonne correction des Fractions
21 novembre 2008 à 19:57:07

Merci ! :)
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
21 novembre 2008 à 20:31:36

Wooow ma version du constructeur en double exposée au yeux de tous :D !
Comme quoi on voit que tes exercices m'ont fait énormément progresser. Encore merci :) !

Dès que j'ai le temps je fais l'exercice de cryptage :) !
22 novembre 2008 à 0:58:22

En fait, j'avais rédigé un truc pas mal plus compliqué mais qui ne fesait intervenir que des opérations mathématiques. Sauf que c'était pas très clair comme manière de faire.

L'avantage de ta méthode c'est qu'elle est explicite, même si je pense qu'on puisse faire plue efficace (c.f. celui de boost), par exemple en remplaçant les pow par autre chose de spécifique pour les puissances de 10. Mais bon, ce n'est pas le but de l'exercice et c'est plus clair comme ça.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
23 novembre 2008 à 19:02:59

Bonjour.

C'est la première fois que je post ici et c'est pour montrer un problème que je ne saurai résoudre.
Le programme des chiffres et formidable mais voyez ce que j'ai eu:
Image utilisateur

10 / 4 est égale à 2,5.
J'aimerai savoir, comment remédier à ça?
23 novembre 2008 à 19:23:33

Fais un nouveau sujet pour poser ta question.
23 novembre 2008 à 19:29:02

dsl je vais le faire mais comme le code été là, j'en profitai :p
23 novembre 2008 à 19:40:31

C'est une bonne idée la cryptologie comme exercice.

Chiffre de César : envoyé !
Anonyme
24 novembre 2008 à 8:31:25

Bonne idée cet exo, je vais essayer de m'y mettre =) (c'est le premier exo que je fais, j'avais un peu décroché du C++ à cause de plusieurs projets web)

Il faut faire un code qui se compile sous tous les OS? Pour savoir si je peux utiliser windows.h pour les couleurs de la console
24 novembre 2008 à 9:59:56

Oui. Sinon, ca ne sert a rien de l'envoyer comme code de correction puisque certaines personnes ne pourront pas l'utiliser.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
24 novembre 2008 à 17:08:01

Et puis pourquoi mettre de la couleur dans la console :D ?
Anonyme
24 novembre 2008 à 19:29:20

Sympas cet exo, il me motive bien et je pense que sa va etre ma premierre participation :)
Mais j'ai une petite question : doit t'on décaler les caractère accentué (é,è,ê,ù,à) et donc les considérer comme leurs équivalents non accentué ?(je pense que oui, mais bon).
Bon aller je vais essayer de voir pour le niveau 2 car ça ne ma pas l'air facile(à moins de faire un espèce de brute force demandant à l'utilisateur à chaque essaie si le décryptage est bon :p (oui, oui je sais c'est pas l'exercice ))
24 novembre 2008 à 20:27:32

Citation : 11

Bon aller je vais essayer de voir pour le niveau 2 car ça ne ma pas l'air facile(à moins de faire un espèce de brute force demandant à l'utilisateur à chaque essaie si le décryptage est bon :p (oui, oui je sais c'est pas l'exercice ))



Si le texte crypté est suffisamment long, l'analyse fréquentielle donne la bonne réponse dans 100% des cas (pas besoin de force brute).

Sinon Nanoc, tu comptes faire la cryptanalyse du chiffre de Vigenère ensuite ? Ça corserait un peu la difficulté, mais rien d'insurmontable ! ^^
Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
24 novembre 2008 à 20:33:25

Citation : 11

Mais j'ai une petite question : doit t'on décaler les caractère accentué (é,è,ê,ù,à) et donc les considérer comme leurs équivalents non accentué ?(je pense que oui, mais bon).



Les caractères accentués ne font pas partis de la table ASCII de base, donc non, de plus, comment savoir après un décalage de 1 par exemple (é => f) faire le contraire (f => é) ?
24 novembre 2008 à 20:55:58

Citation : iNaKoll

Sinon Nanoc, tu comptes faire la cryptanalyse du chiffre de Vigenère ensuite ? Ça corserait un peu la difficulté, mais rien d'insurmontable ! ^^



Oui. D'autres méthodes suivront.

Citation : 11

Sympas cet exo, il me motive bien et je pense que sa va etre ma premierre participation :)



Bienvenue !

Citation : 11

Mais j'ai une petite question : doit t'on décaler les caractère accentué (é,è,ê,ù,à) et donc les considérer comme leurs équivalents non accentué ?(je pense que oui, mais bon).


Non. Les caractères accentués ne sont pas standards et ça poserait trop de problème de les gérer ici.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.