Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

10 janvier 2010 à 17:16:37

=> Lenoa
Vérifie bien si tu gères correctement les boucles dans les boucles, car j'avais le même problème au début parce que j'avais oublié de gérer la priorité des crochets.
  • Partager sur Facebook
  • Partager sur Twitter
10 janvier 2010 à 17:27:11

Pour la priorité des boucles, je considère un fonctionnement plus simple que la pile : je compte la profondeur seulement, et je vais au crochet correspondant.
Le code :

#include <iostream>
#include <string>

using namespace std;

void brainfuck(const char* program, const size_t program_length)
{
    unsigned char* tbl = new unsigned char[30000];
    unsigned char* ptr = tbl;
    const char* act = program;
    while (act != program + program_length)
    {
        if (*act == '>')
        {
            if (ptr != tbl + 30000)
            {
                ++ptr;
            }
            else
            {
                ptr = tbl;
            }
        }
        else if (*act == '<')
        {
            if (ptr != tbl)
            {
                --ptr;
            }
            else
            {
                ptr = tbl + 30000;
            }
        }
        else if (*act == '+')
        {
            ++(*ptr);
        }
        else if (*act == '-')
        {
            --(*ptr);
        }
        else if (*act == '.')
        {
            cout << *ptr;
        }
        else if (*act == ',')
        {
            cin >> *ptr;
        }
        else if (*act == '[')
        {
            if (*ptr == 0)
            {
                size_t deep = 0;
                while (deep != 0 || *act != ']')
                {
                    ++act;
                    if(act > program + 30000)
                    {
                        cerr << "ERREUR: ']' manquant !" << endl;
                        return;
                    }
                    if(*act == '[') ++deep;
                    if(*act == ']' && deep != 0) --deep;
                }
            }
        }
        else if (*act == ']')
        {
            if (*ptr != 0)
            {
                size_t deep = 0;
                while (deep != 0 || *act != '[')
                {
                    --act;
                    if(act < program)
                    {
                        cerr << "ERREUR: '[' manquant !" << endl;
                        return;
                    }
                    if(*act == ']') ++deep;
                    if(*act == '[' && deep != 0) --deep;
                }
            }
        }
        ++act;
    }
    delete[] tbl;
}

int main()
{
    string program =
">++++++++++>+>+["
    "[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>["
        "[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-"
            "[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>"
    "]<<<"
"]";
    brainfuck(program.c_str(), program.length());
    return 0;
}



Pour le moment, je n'ai pas traité l'ouverture de fichier, on verra çà plus tard, il faudrait déjà que çà marche :'(
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
10 janvier 2010 à 18:55:48

Bonjour
Va-t-on avoir un nouvel exercice pour bien commencer 2010 ? :-°
  • Partager sur Facebook
  • Partager sur Twitter
10 janvier 2010 à 19:10:08

J'espère, même si y'a encore du boulot avant d'avoir fini tous les autres ^^
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 16:00:19

Un autre problème (j'ai laissé un peu tomber BF, je posterai un sujet sur le fofo quand j'aurai fini les autres exercices) :
La solution au cassage du cryptage de César me semble ... Mauvaise.
Par exemple, sur un texte sans e (La Disparition de George Perec ?), il plantera lamentablement, a part si je n'ai pas du tout compris son fonctionnement.
Ma solution est de calculer toutes les fréquences, puis de comparer au tableau des fréquences, et de prendre la moins mauvaise solution. A ce que j'aie compris, l'algorithme actuel a une complexité dans le pire des cas de O(NL), avec L la longueur de l'alphabet, et N la longueur de la chaine a décrypter. Le mien a une complexité de O(2N+L²), ce qui me semble bien mieux, avec par exemple un alphabet que l'on estimera a 26 lettres et une longueur de chaîne de 100.000 caractères, cela ferait pour l'algorithme actuel environ 2.600.000 tours, et le mien environ 200.700. Soit une réduction de facteur environ 26 (L) !
Évidemment, pour de petits exemples, c'est l'inverse qui se passe : je fais 702 tours contre 26 avec une chaine de 1 caractère. Mais, même dans ce cas, mon algorithme est meilleur, puisque plus efficace ! Exemple de phrase qui ferait rater l'algorithme de Chlab_lak (je crois) : W'*i*cntn* x'l m*lfnzfa lxfd*. Vous comprendrez que c'est crypté et que je ne vous donne pas la clef. Et que les E ont été remplacés pas des *, juste pour tout faire planter :) ...
Mais; bien évidemment, il est toujours possible de se débrouiller pour faire un message secret encore plus tordu, où choisir A comme lettre principale ne marcherait pas. D'ailleurs, c'est le cas ;) Bon courage pour trouver si vous ne voulez pas de ma solution ^^

Le code source (s'pas ma faute pour les noms, j'ai des goûts bizarres :D ) contient uniquement un fichier, et un namespace anonyme (namespace { } ). La moitié de ce fichier est occupé par le traitement de la ligne de commande, l'autre moitié par le cassage, et le petit bout entre les deux par le cryptage et le décryptage :) .
Il est possible d'utiliser d'autres fréquences de lettres que le français pour le cassage.
Je pense avoir à peu près tout dit, voici le code :

#include <iostream> // std::cin && std::cout && std::getline
#include <fstream>  // std::ifstream && std::ofstream
#include <sstream>  // std::ostringstream
#include <string>   // std::string

#include "cesar.h"

using namespace std;

namespace
{
    /**
     * Met 10 a la puissance v
     *
     * @param v la puissance a laquelle mettre 10
     * @return 10 ^ v
     * @version 1.0
     * @author Lenoa
     */
    short pow10(short v)
    {
        short res = 1; // Initialise res avec 1 (10 ^ 0)
        while (v > 0)  // Tant qu'il faut continuer a augmenter
        {
            res *= 10; // On augmente le resultat
            --v;       // On dit qu'il faut une augmentation de moins
        }
        return res == 1 ? 0 : res; // Si le resultat est 1, on renvoie 0
    }

    /**
     * Recupere l'entier contenu dans s
     *
     * @param s la chaine d'ou extraire l'entier
     * @return l'entier tire de la chaine
     * @version 1.0
     * @author Lenoa
     */
    short get_clef(const char* s)
    {
        short res = 0;
        for (size_t i = 0; s[i] != '\0'; ++i)
        {
            res = pow10(res) + (s[i] - '0');
        }
        return res;
    }
}

/**
 * Affiche le mode d'utilisation de crypto-cesar.
 *
 * @param nom_prog le nom du programme a afficher(defaut : crypto-cesar)
 * @version 1.0
 * @author Lenoa
 */
void usage(string nom_prog = "crypto-cesar")
{
    cerr
    << "Usage : "
    << nom_prog <<
    " [-k NOMBRE]"
    " { -c | -d | -b }"
    " [ { FICHIER_IN | - } [ { FICHIER_OUT | - } ] ]"
    << endl;
}

/**
 * Point d'entree principal du programme.
 *
 * Parse la ligne de commande, puis agit en fonction des arguments trouves.
 * Voir usage pour le synopsis.
 *
 * @param argc le nombre d'arguments dans argv
 * @param argv les parametres de la ligne de commande
 * @return 0 si reussite ; 1 si echec
 * @see usage
 * @see crypt
 * @see decrypt
 * @see casser
 * @version 1.0
 * @author Lenoa
 */
int main(int argc, char** argv)
{
    if (argc < 2)
    {
        usage();
        return 1;
    }
    short clef = 0; // La clef de cesar a utiliser
    char action = ' '; // L'action :
    // 'c' pour crypter, 'd' pour decrypter, et 'b' pour tenter de casser
    string in = "-"; // L'entree indiquee
    string out = "-"; // La sortie indiquee
    if (argv[1][1] == 'k') // Si on fournit une clef
    {
        if (argc < 4) // On demande d'avoir le bon nombre de parametres
        {
            usage();
            return 1;
        }
        clef = get_clef(argv[2]); // On recupere la clef
        action = argv[3][1]; // On recupere l'action
        if (argc >= 5) // Si on a plus de parametres
        {
            in = argv[4]; // Le premier qui suit est le fichier d'entree
            if (argc >= 6)
            {
                out = argv[5]; // Et le suivant est celui de sortie
            }
        }
    }
    else // Sinon, on ne fournit pas la clef, le code est quasiment pareil.
    {
        action = argv[1][1];
        if (argc >= 3)
        {
            in = argv[2];
            if (argc >= 4)
            {
                out = argv[3];
            }
        }
    }
    istream* stream_in; // Le flux d'entree
    ostream* stream_out; // Le flux de sortie

    if (in == "-") stream_in = &cin;
    else stream_in = new ifstream(in.c_str(), ios::in);

    if (out == "-") stream_out = &cout;
    else stream_out = new ofstream(out.c_str(), ios::out | ios::trunc);

    if (action == 'c') // Si on veut crypter
    {
        string s;
        do
        {
            getline(*stream_in, s, '\n');
            *stream_out << crypto::cesar::crypt(s, clef) << endl;
        }
        while (!stream_in->eof());
        // On crypte ligne par ligne et affiche le resultat au fur et a mesure
    }
    else if (action == 'd') // Sinon, si on veut decrypter
    {
        string s;
        do
        {
            getline(*stream_in, s, '\n');
            *stream_out << crypto::cesar::decrypt(s, clef) << endl;
        }
        while (!stream_in->eof());
        // On fait pareil en decryptant
    }
    else if (action == 'b') // Si enfin on veut casser
    {
        string s;
        ostringstream oss;
        do
        {
            getline(*stream_in, s, '\n');
            oss << s << endl;
        }
        while (!stream_in->eof());
        // On recupere toutes les donnees entrees dans oss jusqu'a EOF ou Ctrl+D
        *stream_out << crypto::cesar::casser(oss.str()) << endl; // Et on affiche le resultat
    }
    else // Sinon, il y a un probleme
    {
        usage(); // On affiche l'erreur habituelle
        if (stream_in != &cin) delete stream_in;
        if (stream_out != &cout) delete stream_out; // On detruit les flux
        return 1; // Et on arrete tout
    }
    if (stream_in != &cin) delete stream_in;
    if (stream_out != &cout) delete stream_out; // On detruit les flux
    return 0; // Et on arrete tout
}

#ifndef CESAR_INCLUDED
#define CESAR_INCLUDED

#include <string>

namespace crypto
{
    namespace cesar
    {
        namespace
        {
            /**
             * Definit les frequences des lettres de l'alphabet francais dans
             * l'ordre.
             *
             * Sert de parametre par defaut a crypto::cesar::casser.
             *
             * @enum Frequences
             * @version 1.0
             * @author Lenoa
             */
            const double Frequences[26] =
            {
                9.42, 1.02, 2.64, 3.39, 15.87, 0.95, 1.04, 0.77, 8.41,
                0.89, 0.00, 5.34, 3.24,  7.15, 5.14, 2.86, 1.06, 6.46,
                7.90, 7.26, 6.24, 2.15,  0.00, 0.30, 0.24, 0.32
            }; // Tableau pris sur Wikipedia

            /**
             * Replace v dans la serie des entiers
             *
             * @param v le caractere a replacer
             * @return un entier correspondant a v
             * @version 1.0
             * @author Lenoa
             */
            unsigned char redim(unsigned char v);

            /**
             * Crypte le caractere f par le chiffre de cesar k
             *
             * @param f le caractere a crypter
             * @param k la clef
             * @return f crypte par k selon la methode de cesar
             * @version 1.0
             * @author Lenoa
             */
            unsigned char algcrypt(unsigned char f, short k);

            /**
             * Derypte le caractere f par le chiffre de cesar k
             *
             * @param f le caractere a decrypter
             * @param k la clef
             * @return f decrypte par k selon la methode de cesar
             * @version 1.0
             * @author Lenoa
             */
            unsigned char algdecrypt(unsigned char f, short k);

            /**
             * Calcule la difference entre x et y.
             *
             * @param x
             * @param y
             * @return max(x, y) - min(x, y)
             * @version 1.0
             * @author Lenoa
             */
            double diff(double x, double y);
        }

        /**
         * Crypte la chaine s par le chiffre de cesar c
         *
         * @param s la chaine a crypter
         * @param c le chiffre de cesar
         * @return la chaine cryptee
         * @version 1.0
         * @author Lenoa
         */
        std::string crypt(const std::string& s, short c);

        /**
         * Decrypte la chaine s par le chiffre de cesar c
         *
         * @param s la chaine a decrypter
         * @param c le chiffre de cesar
         * @return la chaine decryptee
         * @version 1.0
         * @author Lenoa
         */
        std::string decrypt(const std::string& s, short c);

        /**
         * Tente de casser le cryptage de cesar
         *
         * @param s la chaine a tenter de casser
         * @param freq le tableau de frequences a utiliser (francais par defaut)
         * @return la chaine decryptee la plus probable
         * @version 1.0
         * @author Lenoa
         * @todo Proposer a l'utilisateur de prendre une autre chaine resultat ?
         */
        std::string casser(
            const std::string& s,
            const double* freq = Frequences
        );
    }
}

#endif // CESAR_INCLUDED

#include "cesar.h"

#include <string>
#include <limits>
#include <locale>

using namespace std;

namespace crypto
{
    namespace cesar
    {

        /**
         * Crypte la chaine s par le chiffre de cesar c
         *
         * @param s la chaine a crypter
         * @param c le chiffre de cesar
         * @return la chaine cryptee
         * @see algcrypt
         * @version 1.0
         * @author Lenoa
         */
        string crypt(const string& s, short c)
        {
            string res(s.length(), ' ');
            for (size_t i = 0; i < s.length(); ++i)
            {
                if (isupper(s[i])) res[i] = algcrypt(s[i], c);
                else res[i] = tolower(algcrypt(toupper(s[i]), c));
            }
            return res;
        }

        /**
         * Decrypte la chaine s par le chiffre de cesar c
         *
         * @param s la chaine a decrypter
         * @param c le chiffre de cesar
         * @return la chaine decryptee
         * @version 1.0
         * @author Lenoa
         */
        string decrypt(const string& s, short c)
        {
            string res(s.length(), ' ');
            for (size_t i = 0; i < s.length(); ++i)
            {
                if (isupper(s[i])) res[i] = algdecrypt(s[i], c);
                else res[i] = tolower(algdecrypt(toupper(s[i]), c));
            }
            return res;
        }



        /**
         * Tente de casser le cryptage de cesar
         *
         * Complexite :
         * o N <- longueur(s)
         * o L <- taille_de_l_alphabet
         * complexite = O(2N + 2L + L²) = O(N+L²)
         * Avec un alphabet restreint (L² << N), cela donne O(N), ce qui est le
         * meilleur resultat possible. Pour un texte francais (alphabet de 26
         * lettres), cela donne O(700 + N) ~= O(N) (En considerant un long
         * texte).
         *
         * Cependant, cette methode de decryptage permet de casser avecun grand
         * taux de reussite :
         * "Le zebre et le yeti mangent des chamallows et des yaourts dans le
         *     wagon."
         * devient, crypte avec un chiffre de 14 :
         * "Zs nspfs sh zs mshw aobusbh rsg qvoaozzckg sh rsg mocifhg robg zs
         *     koucb."
         * Et peut etre casse, malgre les lettres etonnantes.
         * De meme pour : "L'*x*rcic* m'int*r*ss*" (les e sont caches par des *,
         * ce qui gene le cassage par analyse frequentielle que nous utilisons).
         *
         * @param s la chaine a tenter de casser
         * @param freq le tableau de frequences a utiliser (francais par defaut)
         * @return la chaine decryptee la plus probable
         * @version 1.0
         * @author Lenoa
         * @todo Proposer a l'utilisateur de prendre une autre chaine resultat ?
         */
        string casser(const string& s, const double* freq)
        {
            /*
             D'abord, calculer le nombre d'apparitions de chaque lettre et le
             nombre de caractere non speciaux
            */
            size_t apparitions[26] = {0};
            size_t size = 0;
            for (size_t i = 0; i < s.length(); ++i)
            {
                if (isupper(toupper(s[i])))
                {
                    ++apparitions[toupper(s[i]) - 'A'];
                    ++size;
                }
            }
            /*
             Ensuite, calculer les frequences de chaque lettre
            */
            double frequences[26] = {0.};
            for (size_t i = 0; i < 26; ++i)
            {
                frequences[i] = double(apparitions[i]) * 100 / size;
            }
            /*
             Ensuite, calculer la distance par rapport au tableau de frequences
             donne en parametre
            */
            double probabilites[26] = {0.};
            for (size_t i = 0; i < 26; ++i)
            {
                for (size_t j = 0; j < 26; ++j)
                {
                    probabilites[i] += diff(
                                           frequences[
                                               algdecrypt(j + 'A', i) - 'A'
                                           ],
                                           freq[j]
                                       );
                }
            }
            /*
             Enfin, trouver le meilleur decryptage possible
            */
            size_t decalage = 0;
            double min = numeric_limits<double>::max();
            for (short i = 0; i < 26; ++i)
            {
                if (probabilites[i] < min)
                {
                    min = probabilites[i];
                    decalage = i;
                }
            }
            // Et renvoyer le resultat decrypte par le decalage le plus probable
            return crypt(s, decalage);
        }

        namespace
        {
            /**
             * Replace v dans la serie des entiers
             *
             * @param v le caractere a replacer
             * @return un entier correspondant a v
             * @version 1.0
             * @author Lenoa
             */
            unsigned char redim(unsigned char v)
            {
                if (v < 65) return redim(90 - (65 - v) + 1);
                else if (v > 90) return redim(65 + (v - 90) - 1);
                else return v;
            }

            /**
             * Derypte le caractere f par le chiffre de cesar k
             *
             * @param f le caractere a decrypter
             * @param k la clef
             * @return f decrypte par k selon la methode de cesar
             * @version 1.0
             * @author Lenoa
             */
            unsigned char algdecrypt(unsigned char f, short k)
            {
                if (f < 65 || f > 90) return f;
                else return redim(f - k);
            }

            /**
             * Crypte le caractere f par le chiffre de cesar k
             *
             * @param f le caractere a crypter
             * @param k la clef
             * @return f crypte par k selon la methode de cesar
             * @version 1.0
             * @author Lenoa
             */
            unsigned char algcrypt(unsigned char f, short k)
            {
                if (f < 65 || f > 90) return f;
                else return redim(f + k);
            }

            /**
             * Calcule la difference entre x et y.
             *
             * @param x
             * @param y
             * @return max(x, y) - min(x, y)
             * @version 1.0
             * @author Lenoa
             */
            double diff(double x, double y)
            {
                return x < y ? y - x : x - y;
            }
        }
    }
}


Qu'en pensez-vous ?
Nanoc, si tu lis çà, s'il te plait dis-moi ce que tu en penses. Sinon, je t'enverrai un MP avec le code :diable: (menace suprême ! :lol: )

EDIT : Voire le post de Tolf .
RE-EDIT, voire les posts de Goten cette fois-ci
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 16:46:58

Perso, j'en pense que :

1) On utilise que les lettres de l'alphabet, et les étoiles ne font pas partie de l'exercice (mais c'est vrai que ce serait emmerdant :D )

2) S'il n'y a pas de "e" dans le texte, selon moi, aucun problème. C'est la fréquence des lettres qui est utile. A ce que je vois sur ton tableau, la lettre la plus utilisée (le E) a une fréquence d'environ de 16. La deuxième avoisine les 9.5, je pense qu'il y a une sacré marge, et que le programme saurait faire la différence (du moins, je n'ai pas fait l'exercice).

3) J'ai absolument rien capté au deuxième paragraphe :D

Voilà :)
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 16:56:58

C'est dommage que tu ne mettes qu'un seul fichier.

L'intérêt de mettre plusieurs fichiers c'est :
- de déclarer des classes dans les .h et de les implémenter dans des .cpp, ce que tu n'as pas fais donc tu fais pas du c++ :( -> une classe c'est plus propre qu'un namespace privé
- de ne pas recompiler à chaque fois les fichiers non modifiés (pour des gros projets, ça devient lourd), et c'est valable aussi en c -> En gros, si tu modifies juste une ligne de code, tu dois recompiler tout le fichier, donc plus il est petit, mieux c'est.
- d'être clair pour celui qui lit : tu as bien mis des commentaires :) , mais dans un .h, on aurait vu tout de suite la liste des méthodes (ou fonctions) que tu implémentes.
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 17:12:43

@Germanov:

Citation

On utilise les lettres de l'alphabet


Bof, limiter l'exercice à çà ...

Citation

S'il n'y a pas de "e" dans le texte, selon moi, aucun problème. C'est la fréquence des lettres qui est utile. A ce que je vois sur ton tableau, la lettre la plus utilisée (le E) a une fréquence d'environ de 16. La deuxième avoisine les 9.5, je pense qu'il y a une sacré marge, et que le programme saurait faire la différence (du moins, je n'ai pas fait l'exercice).


Justement, mon programme fait çà, celui de Chlab_lak (voir solution) ne le fait pas (à ce que je sache). Et le tableau est tiré de Wikipédia ^^

Citation

J'ai absolument rien capté au deuxième paragraphe :D


S'pas ma faute :-°
Sinon : http://www.siteduzero.com/tutoriel-3-5 [...] grammeur.html ?

@Tolf:
OK, je vais séparer en plusieurs fichier, bien que, pour moi, 500 lignes çà va encore :)
Et pour la liste des fonctions, elle est presque au début du fichier, juste après le namespace anonyme en fait :p
Et pour la partie sur la classe, tout n'est pas OO (citation de lmghs je crois :D ). En fait, les fonctions sont largement suffisantes pour ce cas, dans un namespace crypto::cesar à la limite. Par contre, pour la ligne de commande, çà aurait été mieux de faire une classe, mais j'ai eu la flemme :-°
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 17:20:12

J'ai pas eu le temps de tout regarder, mais euh dans locale t'as std::is(upper|lower), (et dans math t'as pow).
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 17:34:54

Je viens de corriger. Et je vais m'y remettre :)
Pour isupper et islower, j'avais pas vu ... Honte à moi :'(
Et pour le pow de math, j'ai fait une version un peu ... Spéciale, qui n'est pas vraiment un pow.
Donc va pour re-corriger (mais que les isupper et islower) :D
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 17:51:28

Ah et on parle de namespace anonyme... (pas privée).
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 17:54:37

Ah, voilà le nom que je cherchais, merci, j'édite (encore !) :)
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 18:12:32

Je serais toi, je ferais un nouveau sujet...
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2010 à 18:22:36

A la base, c'était pour parler non pas de ma solution, mais du problème de la solution actuelle :D
Donc je vais envoyer un MP à Nanoc contenant ce que j'ai dans mon post, et on verra bien :)
  • Partager sur Facebook
  • Partager sur Twitter
15 janvier 2010 à 0:47:25

En passant, lisez "Histoire des code secrets" (Simon Singh), c'est un bouquin vraiment intéressant sur le sujet et qui regorge d'anecdotes historiques. Ça se lit comme un roman !

http://www.amazon.fr/Histoire-codes-se [...] dp/2253150975

en plus 6.60€ sur amazon !...

Après avoir lu ce bouquin (il y a quelques années) je remarque dans le code d'une des applis de la boîte un transfert de données cryptées qui ramait grave, le mec qui l'avait écrit était très fier de son chiffrement fait maison, très compliqué, inviolable...

Bon bref c'était mathématiquement équivalent à un chiffre de substitution. J'ai donc remplacé son code par une "lookup table". Je lui dis "tiens, j'ai optimisé le chiffrement, l'appli va beaucoup plus vite maintenant"... Il s'est bouffé les couilles un peu.

Une fois le mec viré, on a mis du blowfish...
  • Partager sur Facebook
  • Partager sur Twitter
20 janvier 2010 à 16:34:19

Plus de news de Nanoc ? Ca fait un moment qu'il n'y a pas eu d'exercice :/
  • Partager sur Facebook
  • Partager sur Twitter
20 janvier 2010 à 18:34:18

@Dr. Tenma: Nanoc m'a dit en MP qu'il était en période d'examens, donc un peu occupé ces temps-ci. Il reviendra bientôt, souhaitez-lui bonne chance :)

@bobfuck: Y'a un forum pour raconter sa vie ? :D
  • Partager sur Facebook
  • Partager sur Twitter
26 mars 2010 à 14:21:15

Vous avez des codes assez compliqués pour le BrainFuck quand même non?
Enfin ça tient en 90 lignes pour moi, en dehors des commentaires pour présenter chaque pointeur++ (je trouve ça inutile, avec un nom de variable parlant on s'en passe sans problèmes). Je ne vois pas trop ce que je peux faire de moins que les codes de 300 lignes qui ont été donnés, mais sincèrement expliquez moi, je ne poste pas pour prétendre être le meilleur, sur un code de cette taille la on s'en fou pas mal... Je ne demande pas mieux que vous me donniez des conseils, je poste pour ça.

#include <string>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;

class Env {
	vector<unsigned char> mem;
	int pointeur;
	string instructions;

	int trouverOuvrant(int p){
		int pos = p, nbCrochet = 1;
		while(nbCrochet != 0){
			pos--;
			if(pos < 0) throw string("[ manquant !");
			if(instructions[pos] == '[') nbCrochet--;
			if(instructions[pos] == ']') nbCrochet++;			
		}
		return pos;
	}
	int trouverFermant(int p){
		int pos = p, nbCrochet = 1;
		while(nbCrochet != 0){
			pos++;
			if(pos >= instructions.size()) throw string("] manquant !");
			if(instructions[pos] == ']') nbCrochet--;
			if(instructions[pos] == '[') nbCrochet++;			
		}
		return pos;
	}

	void exec(){
		cout << endl << "---- Debut du programme ---" << endl;

		for(unsigned int i=0; i<instructions.size(); i++){
			switch(instructions[i]){
				case '>': 
					pointeur++; 
					if(pointeur == 30000){ pointeur =0;}
					break;
				case '<':
					pointeur--; 
					if(pointeur == -1){ pointeur = 29999;}
					break;
				case '+':
					mem[pointeur]++; break;
				case '-': 
					mem[pointeur]--; break;
				case '.': 
					cout << mem[pointeur]; break;
				case ',': 
					mem[pointeur] = getchar(); break;
				case '[': 
					if(mem[pointeur] == 0)
						i = trouverFermant(i);
					break;
				case ']':					
					if(mem[pointeur] != 0)
						i = trouverOuvrant(i);
					break;
				default: break;
			}
		}
		cout << "----- Fin du programme ----" << endl;
	}

public:
	Env(string instruc) : mem(30000,0), pointeur(0), instructions(instruc) { exec(); }
};

void main(char argv, char** argc){
	if(argv < 2){
		cerr << "Pas de code a executer !" << endl;
		return;
	}

	ifstream fichier(argc[1]);
	if(!fichier) {
		cerr << "Chemin incorrect !" << endl;
		return;
	}
	string instructions = "", ligne = "";
	while(getline(fichier, ligne)) instructions += ligne;

	try { Env env(instructions); }
	catch(const std::string& e){ cerr << e << endl; }
}


Edit : pas faux pour les recopie de string, corrigé, les instructions sont aussi bien dans une variable de la classe, j'ai modifié.
  • Partager sur Facebook
  • Partager sur Twitter
26 mars 2010 à 14:30:28

Déjà une petite remarque : tu copies la chaîne d'instructions à chaque trouverOuvrant / trouverFermant ... Sinon, je ne vois pas grand chose à redire, si ?
  • Partager sur Facebook
  • Partager sur Twitter
26 mars 2010 à 15:59:39

Pardon, mais pourquoi tu incrémente ton compteur sur '<' et le décrémente sur '>' ?
  • Partager sur Facebook
  • Partager sur Twitter
26 mars 2010 à 16:02:53

C'est une erreur, ce qui est marrant c'est que ça marche quand même en inversé ^^. J'ai remis à "l'endroit"
  • Partager sur Facebook
  • Partager sur Twitter
26 mars 2010 à 17:26:09

Autre remarque pour booster un tout petit peu le code : ++var est plus rapide que var++ de deux copies ;)
cf. opérateurs préfix et postfix.
Mais là on dérive :D
  • Partager sur Facebook
  • Partager sur Twitter
27 mars 2010 à 14:42:15

@Lenoa: C'est vraie, mais négligeable sur des type fondamentale (il me semble)

@Rafale: Regarde les commentaire suivant la solution proposé, Nanoc explique qu'il à choisie ce code pour sa "belle" utilisation de la STL (string, iterator, map, stack, ...). Pour ton code, je trouve pas vraiment cohérent de retourner les erreures d'ouverture du fhichier via cerr et ensuite d'utiliser les exception, utiliser ces dernière tout le temps serait tout aussi simple. Une autre remarque, la taille de la mémoire en BF est constante (3000), un conteneur dynamique n'est pas justifié (std::array en C++11 conviendrait mieux), idm pour parcourir un conteneur (y compris std::string), un itérator convient (même si ici ca ne change rien) (par contre size_t est "plus parlant" que unsigned int pour accéder aux élément d'un conteneur). Sinon j'ai pas regarder en détaille, mais ton approche me semble correcte.
  • Partager sur Facebook
  • Partager sur Twitter
FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
28 mars 2010 à 20:15:35

Pour l'accélération négligeable, çà dépend, si le ++var (ou var++) s'applique 100000 fois (dans le cas d'un code de calcul par exemple), ce n'est pas négligeable ;)

Mais bon, là on part en HS :)
  • Partager sur Facebook
  • Partager sur Twitter
1 avril 2010 à 22:06:39

Citation : Lenoa


Mais, me voilà donc attaquant RLE, et ... Il y a une meilleure compression possible, sauf dans les cas où il y a beaucoup de chiffres ('1', '2', '3', ... ) :

  • Réduire AAAA en 4A
  • Si il n'y a qu'un caractère : A
    • Si c'est une lettre, on la met
    • Si c'est un chiffre, on met 1 suivi du chiffre


Déroulement :

AAAAAUEEE9882
AAAAA => 5A
U     => U
EEE   => 3E
9     => 19
88    => 28
2     => 12
5AU3E192812


On voit que donc lorsqu'il y a beaucoup de chiffres seuls, on a une compression moins bonne. Cependant, leur probabilité d'apparition (dans une image ?) est très faible !
Je trouve donc qu'un système sans flag est plus simple, et plus efficace (dans l'exemple j'obtiens 17 au lieu de 21 caractères (de tête :) )



J'ai pas lu l'exercice mais je vois le principe. Je ne fais que passer et j'ai vu une erreur qui n'a apparemment pas été décelée jusqu'à présent (ou alors, c'est moi qui n'ai pas trouvé le post qui la relevait).

Que se passe-t-il si tu souhaite compresser :
AAAAAAAAAAAAAA9998EE
AAAAAAAAAAAAAA => 14A
999 => 39
8 => 18
EE => 2E
14A39182E
Dans l'autre sens, tu devrais retrouver la chaîne initiale, mais non :
14 => 4
A3 => ??
91 => 111111111
82 => 22222222
E => E

Sinon, tu peux aussi te limiter à des paquets de 9 caractères, ce qui donnerait :
AAAAAAAAAAAAAA => 9A5A
999 => 39
8 => 18
EE => 2E
9A5A39182E
Et là, ça marche. Mais était-ce ce que tu voulais ? Faut voir si c'est si efficace. Regarde si la probabilité d'avoir des chiffres uniques est grande par rapport à la probabilité d'avoir un gros paquet du même caractère. Si c'est le cas, alors c'est négligeable d'avoir à se limiter à 9 caractères devant la perte de mémoire due aux chiffres uniques.
  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2010 à 12:25:35

Même avec l'astuce flag, il faut se limiter à 9 si j'ai bien lu le sujet. Et oui, je limite à 9.

Le code :

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

string compress(const string& from)
{
    string res;
    for (size_t i = 0; i < from.length(); ++i)
    {
        char c = from[i];
        size_t j = i;
        while (j < from.length() && from[j] == c) ++j;
        size_t len = j - i;
        while (len > 0)
        {
            if (len == 1)
            {
                if (c != '0'
                        && c != '1'
                        && c != '2'
                        && c != '3'
                        && c != '4'
                        && c != '5'
                        && c != '6'
                        && c != '7'
                        && c != '8'
                        && c != '9')
                {
                    --len;
                    res += c;
                }
                else
                {
                    --len;
                    res += '1';
                    res += c;
                }
            }
            else if (len >= 9)
            {
                len -= 9;
                res += '9';
                res += c;
            }
            else
            {
                res += len + '0';
                res += c;
                len = 0;
            }
        }
        i = j - 1;
    }
    return res;
}

string decompress(const string& from)
{
    string res;
    for (size_t i = 0; i < from.length(); i += 2)
    {
        if (from[i] == '0'
                || from[i] == '1'
                || from[i] == '2'
                || from[i] == '3'
                || from[i] == '4'
                || from[i] == '5'
                || from[i] == '6'
                || from[i] == '7'
                || from[i] == '8'
                || from[i] == '9')
        {
            size_t n = from[i] - '0';
            for (size_t j = 0; j < n; ++j)
            {
                res += from[i + 1];
            }
        }
        else
        {
            res += from[i];
            --i;
        }
    }
    return res;
}

int main(int argc, char** argv)
{
    if (argc < 3)
    {
        cerr << "Usage : rle {-c|-d} FILE" << endl;
        return 1;
    }
    char action = argv[1][1];
    string input_filename = argv[2];
    ifstream input_file(input_filename.c_str(), ios::in);
    if (!input_file)
    {
        cerr << "Unable to open file '" << input_filename << "' ..." << endl;
        return 1;
    }
    string input, output;
    string s;
    while (getline(input_file, s))
    {
        input += s + '\n';
    }
    if (action == 'c')
    {
        ofstream o((input_filename + ".rle").c_str(), ios::out | ios::trunc);
        cout << "Compression en cours ..." << endl;
        string output = compress(input.substr(0, input.length() - 1));
        o << output;
        cout << "Compression terminee sans erreur. "
        << "Le fichier fait "
        << static_cast<double>(output.length())
        / (input.length() - 1)
        * 100
        << "% de sa taille originale." << endl;
        o.close();
    }
    else if (action == 'd')
    {
        ofstream o(
            input_filename.substr(0, input_filename.length() - 4).c_str(),
            ios::out | ios::trunc
        );
        cout << "Decompression en cours ..." << endl;
        string output = decompress(input.substr(0, input.length() - 1));
        o << output;
        cout << "Decompression terminee sans erreur. "
        << "Le fichier fait "
        << static_cast<double>(output.length())
        / (input.length() - 1)
        * 100
        << "% de sa taille compressee." << endl;
        o.close();
    }
    else
    {
        cerr << "Action '" << action << "' isn't valid ..." << endl;
        return 1;
    }
    input_file.close();
    return 0;
}



C'est simple, je trouve ;)

EDIT : J'avais pas tout lu :
La probabilité d'avoir des chiffres uniques est de <math>\(\frac{10}{256}\ \times\ \frac{255}{256}\ =\ \frac{2550}{256^2}\)</math>. La probabilité d'avoir des chiffres au moins en double est de <math>\(\frac{10}{256}\ \times\ \frac{1}{256}\ =\ \frac{10}{256^2}\)</math>
Donc il n'y a que des probabilités très faibles d'avoir des chiffres. Et les chiffres en double ne sont pas beaucoup plus durs à trouver que les chiffres seuls. Donc le format de compression me semble meilleur avec mon système, en tout cas pas pire.
  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2010 à 12:49:09

@Lenoa : le code serait plus court (et pas moins lisible) ainsi ;) :

string compress(const string& from)
{
    string res;
    for (size_t i = 0, l = from.length(); i < l; ++i)
    {
        char c = from[i];
        size_t j = i;

	for(; j < l && from[i] == c; ++j);

        size_t len = j - i;

        while (len > 0)
        {
            if (len == 1)
            {
		int d = c - '0';
                if (d >= 0 && d <= 9)
                {
                    --len;
                    res += c;
                }
                else
                {
                    --len;
                    res += '1';
                    res += c;
                }
            }
            else if (len >= 9)
            {
                len -= 9;
                res += '9';
                res += c;
            }
            else
            {
                res += len + '0';
                res += c;
                len = 0;
            }
        }

        i = j - 1;
    }
    return res;
}

string decompress(const string& from)
{
    string res;
    for (size_t i = 0, l = from.length(); i < l; i += 2)
    {
	char c = from[i];
	int d = c - '0';

        if (d >= 0 && d <= 9)
        {
            for (size_t j = 0, n = c - '0'; j < n; ++j)
            {
                res += from[i + 1];
            }
        }
        else
        {
            res += c;
            --i;
        }
    }
    return res;
}


(Remplacement des multiples conditions des if par deux conditions à chaque fois et stockage de from.length() pour éviter de rappeler la méthode à chaque fois.

Edit : dans compress() tu pourri utiliser string::find_last_of ou string::find_first_not_of
  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2010 à 13:04:50

Oui, on peut toujours faire mieux, et j'avais fait çà il y a un bon moment (et tu ne gagner que 17 lignes ;) )
J'ai même encore quelques idées pour booster :
int d = c - '0';
if(d >= 0 && d <= 9)
// devient :
if(c >= '0' && c <= '9')

Bref, juste pour dire qu'on peut toujours raccourcir, mais que là n'est pas le sujet ;)
  • Partager sur Facebook
  • Partager sur Twitter
2 avril 2010 à 16:17:52

Oui oui c'était juste deux petites remarques comme ça ;)
  • Partager sur Facebook
  • Partager sur Twitter