0

Сразу говорю, что мой алгоритм не претендует на лучшее решение, я просто не могу понять, почему он правильно узнает только пробел (в 100% случаев), а остальные буквы как повезет (чем больше текст, тем точнее). Как мне исправить его, чтобы он работал верно?

Функция алгоритма:

#include "header.h"

using namespace std;

void frequency (ifstream & in, ofstream & out){
    map<char,int> fr;
    map<char,char> final_fr;
    map <char, int> :: iterator it = fr.begin();
    map <char, char> :: iterator it_final = final_fr.begin();
    char temp = 0;
    char letters[] = {' ', 'e', 't', 'a', 'o', 'i', 'n', 's',
        'h', 'r','d', 'l', 'c', 'u', 'm', 'w', 'f', 'g', 'y', 'p',
        'b', 'v','k', 'x', 'j', 'q', 'z' };
    //формируем карту из всех символов ascii и счетчика (им будем подсчитывать количество встреченных символов (встретили символ - +1)
    for (int i = 0; i < 255; i++){
        fr.insert(make_pair(char(i),0));
    }
    //считываем файл, сравнивая приходящие из потока значения с ключами мапы
    while (!in.eof()){
        temp = in.get();
        it = fr.find(temp);
        if (it != fr.end()){
            (it->second)++;
        }
    }
    //формируем из полученной мапы с частотностью вектор пар, дабы было легче сортировать
    vector<pair<char,int>> arr_p;
    arr_p.reserve(10);
    for (map <char, int> :: iterator i = fr.begin(); i != fr.end(); i++){
        arr_p.push_back(make_pair(i->first, i->second));
    }
    //сортировка вставками упомянутого вектора
    for (int i = 1; i < arr_p.size (); i++) {
        for (int j = i - 1; (j > 0) && (arr_p[j].second > arr_p[j + 1].second); j--) {
            swap (arr_p[j], arr_p[j + 1]);
        }
    }
    //формирование новой мапы из сортированного вектора(наиболее встречаемые символы лежат в конце) и массива английских букв,
    //изначально отсортированных в порядке встречаемости
    vector<pair<char,int>>::reverse_iterator iter = arr_p.rbegin();
    for (int i = 0; i < 32 && (iter != arr_p.rend()); i++, iter++){
        final_fr.insert(make_pair(iter->first, letters[i]));
    }
    //считываем заново файл, сравнивая приходящие знаки с ключами мапы (если нашли совпадение, то пишем в файл пару ключа - настоящее значение знака)
    in.clear ();
    in.seekg (0, ios::beg);
    while (!in.eof()){
        temp = in.get();
        it_final = final_fr.find(temp);
        if (it_final != final_fr.end()){
            out << it_final->second;
        }
    }
}

Мейн:

int main(int argc, char** argv) {
    ifstream fin;
    ofstream fout;
    int key = 3;
    fin.open("C:\\Users\\user\\Desktop\\output1.txt");
    fout.open("C:\\Users\\user\\Desktop\\input1.txt");
    frequency(fin, fout);
    fin.close();
    fout.close();
    return 0;
}

Заголовок(на всякий):

#pragma once
#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>

void frequency (std::ifstream & in, std::ofstream & out);
Nabla
  • 133
  • Потому что это статистика. Тут не бывает точных результатов. Не может произвольный текст точно соответствовать по статистике некому эталону. Частотный анализ лишь один из инструментов, который может помогать в работе аналитику. – Mike Sep 18 '18 at 19:41
  • Но мой алгоритм Очень плохо угадывает. Я могу лишь понять, из каких слов состоит текст(длина слова), а прочитать ничего не могу! Как мне сделать это точнее, намного точнее? – Nabla Sep 18 '18 at 19:44
  • 1
    Это невозможно опираясь только на частотный анализ отдельных букв. Серьезные алгоритмы должны учитывать частоту сочетаний букв в тех или иных местах слов (например распространенные окончания). И это только для грубой оценки. После нее программа должна пользуясь словниками используемого языка смотреть допустимость слов и при большом количестве недопустимых менять предположения относительно определенных букв. – Mike Sep 18 '18 at 19:48
  • 1
    Вот тут еще описана пара возможных приемов https://ru.stackoverflow.com/q/467831/194569 – Mike Sep 18 '18 at 20:02
  • [Тяжко вздыхая] - интересно, когда-нибудь танцы на граблях while (!in.eof()) закончатся?... – Harry Sep 19 '18 at 03:08

1 Answers1

1

Написать ручную сортировку, когда есть готовый std::sort? Интересно.

В целом, на небольших выборках в 200-300 слов частотный алгоритм не дает хорошего результата. Можно специально сделать такой текст, в котором будет чаще встречатся какая то буква. К примеру, в тексте будет упоминатся Фома и все, буква ф, которая в русском языке редко встречается, может выйти на первое место. Поэтому, обычно само сопоставление делает уже человек-лингвист.

Но! есть чудные способы улучшить. К примеру, одиноко стоящей обычно бывает буква а или в. А вот ц или п крайне редко.

Второй способ состоит в том, что не нужно делать частотность буквенную, а нужно делать частотность по парам букв. Есть очень много пар, которые даже для неграмотного написания почти невозможны. к примеру, гласная+мягкий знак.

Я когда то делал частотность по тройкам и это работало просто великолепно. Уже на текстах в сотню букв угадывало до половины алфавита. И потом конечно ручной анализ + словарь.

KoVadim
  • 112,121
  • 6
  • 94
  • 160
  • А где вы взяли частотность по парам/тройкам(имеется в виду статистика)? – Nabla Sep 18 '18 at 19:56
  • @Nabla Любая частотность берется из прогона точно такой же программы на эталонных текстах. На большом количестве текстов. И на забывайте что она может тоже меняться от стиля автора и назначения текста, так что не стоит использовать только художественную литературу. Недопустимые (обычно, вы же знаете русское слово с 3 буквами "e" подряд ?) комбинации можно собрать просто по словникам – Mike Sep 18 '18 at 19:59
  • взял несколько различных текстов и прогнал кодом. Для русского текста брал войну и мир. Главное, это то, что в тройках уже не частотность важна, а на само наличие/отсутствие. А само наличие делится на много-мало. И все. – KoVadim Sep 18 '18 at 20:02
  • Т.е. алгоритм такой: 1) создаю массив из разрешенных троек 2) прогоняю несколько больших книг через прогу, которая занимается подсчетом встретившихся троек 3) после подсчета сортируем некий эталонный массив с убывающими по популярности тройками 4) считаем в шифртексте количество различных троек, затем сопоставляем эталон с тем, что мы насчитали и делаем замену на "правильные" тройки 5) пишем правильные тройки в файл. Верно ли это? – Nabla Sep 18 '18 at 20:12
  • @Nabla И упретесь в ту же проблему, текст с Фомой. Думаю тройка Фом встречается редко просто потому, что ф встречается редко. Скорее перебор вариантов подстановки алфавита (исходя из частот одиночных букв +-) и просмотр текста после прогона по такому алфавиту на допустимые/не допустимые тройки – Mike Sep 18 '18 at 20:19
  • 1 этап - это результат второго. А вот дальше начинается интересное. Пытаемся угадать хотя бы одну букву. Но если нет - то нет. В любом случае ищем подходящие варианты на первое место, подставляем получившиеся буквы. Пробуем подобрать 4 букву и так рекурсивно. Обычно, после первых двух-трех слов уже бывает достаточно букв. А дальше применяется поиск по маске слов, в которых максимально найдено букв. Также, не стоит забывать, что можно искать удвоения и утроения. Слов с утроением не так уж и много:) – KoVadim Sep 18 '18 at 20:25