Quand on débute, la lecture d'un document comme celui-ci peut décourager, non ? 1320 pages de description du C++.
Sinon, je viens de faire l'exercice du chiffre de césar, je me sens particulièrement content du cassage (réussit à casser même des messages dont les e ont été remplacés par des *, ce que d'autres ne permettent pas, cf. le commentaire dans cesar.cpp).
Le code (à nouveau un peu de c++11, particulièrement shared_ptr) :
#ifndef CESAR_HPP_INCLUDED
#define CESAR_HPP_INCLUDED
#include <string>
#include <utility>
namespace cesar
{
extern const double Frequencies_FR[];
std::string crypt(const std::string & s, short k);
std::string decrypt(const std::string & s, short k);
/**
* 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
*
* @author Equinoxe
*/
std::pair<std::string, int> crack(
const std::string & s,
const double * freq = Frequencies_FR
);
}
#endif // CESAR_HPP_INCLUDED
#include "cesar.hpp"
#include <cmath>
#include <string>
#include <limits>
#include <locale>
static char mod(char a, int m) {
while (a < 0) a += m;
a %= m;
return a;
}
static char docrypt(char c, short k)
{
if (std::isupper(c)) return mod((c - 'A' + k), 26) + 'A';
if (std::islower(c)) return mod((c - 'a' + k), 26) + 'a';
if (std::isdigit(c)) return mod((c - '0' + k), 10) + '0';
return c;
}
namespace cesar
{
const double Frequencies_FR[] =
{
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
}; // Thanks Wikipedia FR !
std::string crypt(const std::string & s, short k)
{
std::string res(s.length(), ' ');
for (size_t i = 0; i < s.length(); ++i)
res[i] = docrypt(s[i], k);
return res;
}
std::string decrypt(const std::string & s, short k)
{
return crypt(s, -k);
}
/**
* 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 avec un 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 peu courantes.
* 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 et la clef ayant permis
* de casser
*
* @author Equinoxe
* @todo Proposer a l'utilisateur de prendre une autre chaine resultat,
* voire un tableau des 26 resultats possibles, trié par ordre de
* probabilite ?
*/
std::pair<std::string, int> crack(const std::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 (short i = 0; i < 26; ++i)
{
frequences[i] = apparitions[i] * 100. / size;
}
/*
Ensuite, calculer la distance par rapport au tableau de frequences
donne en parametre
*/
double probabilites[26] = {0.};
for (short k = 0; k < 26; ++k)
{
for (char c = 'A'; c <= 'Z'; ++c)
{
probabilites[k] += std::abs(
frequences[
docrypt(c, -k) - 'A'
]
-
freq[c - 'A']
);
}
}
/*
Enfin, trouver le meilleur decryptage possible
*/
size_t decalage = 0;
double min = std::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 std::make_pair(crypt(s, decalage), 26 - decalage);
}
}
Qu'en pensez-vous ?
Pensez-vous qu'il serait intéressant de mettre ma version du cassage en solution, étant donné qu'elle est plus performante que celle proposée par la solution actuelle ?
Euh ... C'est-à-dire que ... En fait ... Bah ...
C'est une assiette de morpion !
En fait, si on veut être précis, ce que je dessine c'est une plaque.
Parce que, après tout, il n'y a pas de champ de morpion.
Est-ce que la dénomination est vraiment importante ?
Parce que je dois admettre que j'avais commencé à coder en français, et que, parce que finalement je préférais l'anglais, j'ai cherché à modifier le programme en en faisant le moins possible. Du coup, j'ai juste cherché un mot proche, sans me soucier de la traduction finale.
Et, pour le cas, mon message précédent était une maigre tentative d'humour.
Là, j'en suis sûr, j'avais commencé par mettre CanPlay avant de réfléchir.
En effet, on ne demande pas si on a la capacité de jouer (ce qui est toujours le cas : un utilisateur peut toujours appuyer sur les boutons qu'il veut), mais si on a le droit de jouer, suivant les règles du jeu.
Donc c'est bien MayPlay.
Fail.
Edit :
D'ailleurs, pour les notes de difficulté, je propose un pour TicTacToe, trois pour le chiffre de césar (deux plus un de cassage), quatre pour le chiffre de vigénère (Le cassage est particulièrement tordu), et deux pour le cryptage XOR, parce qu'il n'y a que cryptage/décryptage à gérer.
Là, j'en suis sûr, j'avais commencé par mettre CanPlay avant de réfléchir.
En effet, on ne demande pas si on a la capacité de jouer (ce qui est toujours le cas : un utilisateur peut toujours appuyer sur les boutons qu'il veut), mais si on a le droit de jouer, suivant les règles du jeu.
Donc c'est bien MayPlay.
Fail.
Edit :
D'ailleurs, pour les notes de difficulté, je propose un pour TicTacToe, trois pour le chiffre de césar (deux plus un de cassage), quatre pour le chiffre de vigénère (Le cassage est particulièrement tordu), et deux pour le cryptage XOR, parce qu'il n'y a que cryptage/décryptage à gérer.
1) C'est can. désolé !
une fonction qui s'appelle mayPlay devrait retourner la probabilité que l'on joue ici.
Ou bien alors c'est que tu es vraiment trop poli pour un programmeur quand tu demandes la permission.
Can = capacité / Autorisation
(ex: Can I have a shower ?)
2)Pour les difficultés : je vais regarder de plus près les exercices en question...
J'ai fini l'exercice du chiffre de vigénère, il est vraiment compliqué de trouver les formules pour la longueur de la clef (bon, pour la recherche de la clef elle-même, ce n'est pas plus dur que le chiffre de césar).
Je maintiens donc 4 de difficulté.
Ma solution (encore améliorable, j'ai juste fait quelque chose de fonctionnel) :
main.cpp :
#include "Vigenere.hpp"
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
typedef std::map<std::string, std::string> option_map;
void parseOpts(option_map & map, int argc, char** argv);
bool hasOpt(const option_map & map, const std::string & opt) {
return map.find(opt) != map.end();
}
std::string & getOpt(option_map & map, const std::string & opt) {
return map[opt];
}
typedef std::shared_ptr<std::istream> Istr;
typedef std::shared_ptr<std::ostream> Ostr;
void NoOpDeleter(void *) {}
Istr inStr(const std::string & str) {
if (str == "-") {
return Istr(&std::cin, NoOpDeleter);
} else {
return Istr(new std::ifstream(str));
}
}
Ostr outStr(const std::string & str) {
if (str == "-") {
return Ostr(&std::cout, NoOpDeleter);
} else {
return Ostr(new std::ofstream(str));
}
}
enum class Action { Unknown, Crypt, Decrypt, Crack };
void usage(const std::string & prog = "vigenere")
{
std::cerr <<
"Usage : " << prog <<
" [ -k KEY ]" <<
" { -c | -d }" <<
" [ -i { FILE_IN | - } ]" <<
" [ -o { FILE_OUT | - } ]" <<
std::endl;
std::cerr <<
"-c : Crypt" << std::endl <<
"-d : Decrypt" << std::endl <<
"-b : Break without the key." << std::endl <<
" Then the key could whether be an integer," << std::endl <<
" the known length of the key, whether be" << std::endl <<
" absent." << std::endl <<
std::endl <<
"-i : Input from the file FILE_IN, or from standard" << std::endl <<
" input, if '-' is provided. Default '-'." << std::endl <<
"-o : Output to the file FILE_OUT, or to standard" << std::endl <<
" output, if '-' is provided. Default '-'." << std::endl <<
std::endl <<
"-k : The key with which cipher. Only alphabetic" << std::endl <<
" characters should be placed, otherwise the" << std::endl <<
" result is undefined. If -b is provided, then" << std::endl <<
" it could whether represent the length of the" << std::endl <<
" key, whether be absent. However, it is" << std::endl <<
" compulsory with -c or -d." << std::endl <<
std::endl;
}
#define FAIL \
usage (argv[0]); \
return EXIT_FAILURE
#define ASSERT(C) \
if (!(C)) { \
FAIL; \
}
int main(int argc, char** argv)
{
if (argc < 1) {
usage();
return EXIT_FAILURE;
}
option_map map;
parseOpts(map, argc, argv);
Action action = Action::Unknown;
if (hasOpt(map, "-c")) {
action = Action::Crypt;
ASSERT(!hasOpt(map, "-d") && !hasOpt(map, "-b"));
} else if (hasOpt(map, "-d")) {
action = Action::Decrypt;
ASSERT(!hasOpt(map, "-b"));
} else if (hasOpt(map, "-b")) {
action = Action::Crack;
}
ASSERT(action != Action::Unknown);
std::string key = getOpt(map, "-k");
ASSERT(action == Action::Crack || key != "");
std::string inS = getOpt(map, "-i");
if ( inS == "") inS = "-";
std::string outS = getOpt(map, "-o");
if (outS == "") outS = "-";
Istr in = inStr( inS);
Ostr out = outStr(outS);
Vigenere V(key);
if (action != Action::Crack) {
while (in->good()) {
std::string tmp;
std::getline(*in, tmp);
if (action == Action::Crypt) {
*out << V.Crypt(tmp) << std::endl;
} else if (action == Action::Decrypt) {
*out << V.Decrypt(tmp) << std::endl;
}
}
} else {
std::string s;
while (in->good()) {
std::string tmp;
std::getline(*in, tmp);
s += tmp + "\n";
}
int keyLen = -1;
if (key != "") {
std::istringstream iss(key);
iss >> keyLen;
}
std::pair<std::string, std::string> result = V.Crack(s, keyLen);
*out << result.first;
std::cout << "=================================================="
<< std::endl
<< "Key : " << result.second << std::endl;
}
return EXIT_SUCCESS;
}
void parseOpts(option_map & map, int argc, char** argv)
{
for (int argi = 1 ; argi < argc ; argi += 2) {
std::string arg = argv[argi];
std::string value = "";
if (argi + 1 < argc) {
std::string nextArg = argv[argi + 1];
if (nextArg[0] != '-') {
value = nextArg;
} else {
--argi;
}
}
map.insert(std::make_pair(arg, value));
}
}
Edit2 : Le Cowsay est plutôt simple aussi, je le mettrais à un et demi. N'empêche qu'il faudrait au moins une autre personne qui se dise d'accord (ou pas) avec moi, que je commence à mettre les notes de difficulté.
main.cpp :
#include <iostream>
#include <string>
#include <vector>
const int MAX_CHAR = 50;
const std::string IMAGE =
" \\ ^__^\n"
" \\ (oo)\\_______\n"
" (__)\\ )\\/\\\n"
" ||----W |\n"
" || ||\n";
int main(int argc, char ** argv)
{
std::string str;
for (int argi = 1 ; argi < argc ; ++argi) {
str += argv[argi];
str += " ";
}
str.resize(str.size() - 1);
std::vector<std::string> lines;
for (size_t i = 0 ; i < str.length() ; ++i) {
if (i + MAX_CHAR >= str.length()) {
lines.push_back(str.substr(i));
break;
}
size_t j = str.find_last_of(" \t\n", i + MAX_CHAR);
if (j == std::string::npos || j <= i) {
lines.push_back(str.substr(i, MAX_CHAR));
i += MAX_CHAR;
} else {
lines.push_back(str.substr(i, j - i));
i = j;
}
}
size_t maxLen = 0;
for (std::string & s : lines)
if (s.length() > maxLen)
maxLen = s.length();
std::string sep(maxLen + 2, '-');
std::cout << "/" << sep << "\\" << std::endl;
for (std::string & s : lines) {
std::cout << "| " << s;
for (size_t i = s.length() ; i < maxLen ; ++i) {
std::cout << " ";
}
std::cout << " |" << std::endl;
}
std::cout << "\\" << sep << "/" << std::endl;
std::cout << IMAGE << std::endl;
return EXIT_SUCCESS;
}
Pour le cowsay il est pas si simple que ça : je ne sais pas si t'as vu le nombre d'entités de la STL que tu as employée... je dirais au moins 2.
Pour Vigénère, t'as raison
XOR 3, c'est parfait.
Pas de demi, sinon on va se retrouver avec des quarts et des huitièmes, ce sera le bazar.
ok.
Pour le Cowsay je dirais 2.
edit : je propose qu'on discute des difficultés sur le méta...
Franchement, pour cowsay, j'aurais mis plus de 2 (3 peut-être) car normalement, à ce que j'avais compris, on doit aussi gérer les différents paramètre du main.
Et pour Vigenère, j''aurais mis 3 aussi. Il me semble plutôt simple non ? C'est seulement le 3em Niveau qui mérite 4.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles- ♡ Copying is an act of love.
Ah, zut, je n'avais pas vu ton message, @che. Mon message signifiait "J'ai ajouté les images de non-difficulté". D'ailleurs, elles sont bien comme ça ?
Pour cowsay, les paramètres du main ne sont pas vraiment importants : il suffit de récupérer le premier. C'est fait en deux lignes (check de argc et récupération de argv[1]). Si on veut améliorer, on peut concaténer tous les arguments avec un espace entre chaque.
Et vigénère mérite plus que césar, puisque son niveau 1 & 2 sont globalement aussi durs que césar, et que son niveau 3 est plus dur.
Pour moi, mettre cowsay et vigénère ensemble serait une hérésie. (D'ailleurs, j'entame la correction du triangle de pascal, plutôt facile à faire pour une première correction).
peut être que tu devrait mettre la petite flamme en gris ou en couleur normale et faire un truc du genre : IIIIIIIIII
en remplacant le jaune par les flammes colorés et le rouge par les flames en niveau de gris.
EDIT : dsl j'avait pas vu
par contre on devrait plutôt noter sur 10 pasque la la dificulté max est atteinte rapidement ...
Disons qu'à mon avis le chiffre de Vigénère mérite bien ses quatre sur cinq.
Mais il serait en effet possible de mettre des demis, pour avoir au final une note ayant dix possibilités, sans avoir à élargir les tableaux (qui risqueraient de ne plus rentrer dans les plus petites résolutions).
Je propose que la suite de la discussion se fasse là-bas.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.