Как стать автором
Обновить
3
0
Александр @Acaunt

Пользователь

Отправить сообщение

std::vector контейнер предназначенный для выделения блока памяти вот смотри:

[1][2][3][4][5]
       ^
ты хочешь удалить этот объект
что происходит в векторе:
1шаг: 4 элемент перемещаем в 3
обычно при этом в объекте ктороый перемещают затирается его старая информация
[1][2][4][0][5]
тепрь старый 4 элемент пустой но он ещё существует
2шаг: 5 элемент перемещаем в 4 элемент
[1][2][4][5][0]
теперь старый 5 элемент обнулен но всё ещё находится в памяти
3шаг: вызываем деструктор у последнего элемента
[1][2][4][5][NULL]
последний элемент удалён но нужно помнить что в векторе память уже была выделена память под 5 элементов
поэтому последний элемент всё ещё занимает память но он не инециализирован

если тебя смущает это то тебе необходимо использовать std::list или std::forward_list

list
       --> --> --> --> -->
NULL [1] [2] [3] [4] [5] NULL
   <-- <-- <-- <-- <--

тут например каждый элемент имеет связь со своими соседями, но при этом они находятся в разных участках памяти

удаление происходит так:

list
       --> --> --> -->
NULL [1] [2] [4] [5] NULL
   <-- <-- <-- <--

тоесть соседи у удаляемого объекта забывают про него и знакомятся с новым соседом

а std::forward почти как std::list но объекты не имеют связи с предыдущими соседями тоесть:

forward_list
  --> --> --> --> -->
[1] [2] [3] [4] [5] NULL

Но из-за такого распределения памяти выходит проблематичным получение произвольного объекта по индексу

Лучше изучите стандартные контейнеры STL

Добавь к классу Object ещё конструкторы и операторы копирования, перемещения с выводом в консоль запусти свой код ещё раз и посмотри результат.

П.С. при использовании метода erase все последующие объекты применяют методы перемещения чтобы занять место в памяти у удаляемого объекта. И так получается, что на место последнего никого нет, но память нужно освободить. Вот и вызывается деструктор у последнего.

А понял. Да пробовал решалось. Так как в моем алгоритме используется перебор когда больше нет единственного варианта.

Ах да ещё не забудь проверить работоспособность свой алгоритм в случае если судоку выдан с ошибкой, изначально.

В каком смысле на Эвересте?

П.С. я для тестов алгоритма брал из интернета самые сложные варианты судоку.

Вряд ли смогу точно сказать, но по-моему меньше 5 секунд для 25х25. А для 36х36 я просто ждал около минуты ничего не происходило, ну и выключал программу

Читал, читал и как то не понял прикола с опорными точками. Ты типо собираешься относительно опорных точек подбирать значения?

Я как то давно делал авторешатель судоку. (Только я работал с кодом на C++, но это не важно).

У меня было несколько этапов:

1) сбор полной информации о всех свободных клеток (при этом собирал я их в разные массивы: общий где абсолютно все клетки, клетки находящиеся в одном большом квадрате, по горизонтали, по вертикали. получились 4 блока для анализа)

2) для каждой свободной клетки я собирал массив из возможных вариантов (для этой цели я использовал массив битов, для того чтобы хоть как-то сэкономить на памяти, то есть соответственно 0 бит отвечал за число 1, 2 бит за число 3 ну и так далее)

3) я проводил анализ каждого блока (из 1 этапа) для массива, где хранятся все свободные клетки, я искал клетки у которых имеется единственное возможное значение после чего записывал его и удалял клетку из всех блоков. Для остальных блоков была задача на поиск единственного возможного значения, которого нет в других клетках, ну и снова записывал его и удалял из всех блоков.

4) провожу проверку на то что решено (индикатором являлся размер 1 блока)

5) проверяю на ошибки (в возможных вариантах клетки отсутствуют варианты, это единственный способ проверки мне тогда пришел в голову)

6) если в 3 этапе была совершена хотя бы 1 запись то возвращался к 3 этапу, в противном случае я искал клетку с минимально возможным количеством вариантов. (Самый желанный вариант это клетка с 2 возможными значениями) Копировал всё данные и в копию подставлял возможный вариант и отправлял копированные данные на 3 этап и так далее пока не решится или не обнаружится ошибка.

В принципе мой алгоритм справлялся с судоку размером 25х25, но с 36х36 уже слишком долго выполняется.

Сейчас у меня есть пара мыслей как можно оптимизировать алгоритм, но если честно просто лень

Статья понравилась, жалко у меня не хватает кармы, чтобы повысить вам карму.

Узнал немного нового о compile time. Даже появилась идея, как возможно можно решить одну старую задумку, которая у меня до этого не получалась.

Когда-то давно тоже делал игру в жизнь, но с возможностью менять правила жизни мира в процессе выполнения.

П.С. Спасибо за статью)

Прикольно, но хотелось бы больше получить информации о реализации внутренней структуры кода. (Да я увидел ссылку на гитхаб, но мне бы хотелось узнать мысли о том как пришёл к такому решению) хотя наверное это не совсем статья это больше самореклама.

Ещё я так понял у тебя логгер поддерживает только строки, что не всегда удобно, если например нужно для отладки отправить числа какие-нибудь с сообщением что конкретно происходит. Это конечно можно решить с помощью конкатенации строк и чисел, но всё равно не совсем удобно.

Я например делал свой логгер, пару месяцев назад (ещё не доделал) вот пример моей реализации:

Скрытый текст
#include <iostream>

#include <condition_variable>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <thread>
#include <atomic>
#include <mutex>
#include <queue>
using namespace std;

class Logger {
private:
    enum Level_log : uint8_t {
        info,
        debug,
        warning,
        error
    };
    
private:
    template<uint8_t>
    struct Level {};
    
public:
    using Info = Level<Level_log::info>;
    using Debug = Level<Level_log::debug>;
    using Warning = Level<Level_log::warning>;
    using Error = Level<Level_log::error>;
    
public:
    class Message {
    private:
        uint8_t level_log;
        ostringstream message;
        
    public:
        template<uint8_t lvl>
        Message(const Level<lvl>& id)
            : level_log(lvl) {
        }
        
        Message(Message&&) = default;
        
        ~Message() {
           if (message.tellp() != 0) {
               Logger::get_instance().add_to_queue(level_log, move(message.str()));
            }
        }
        
    public:
        template<class Text>
        Message& operator<<(const Text& text) {
            message << text;
            return *this;
        }

        Message& operator<<(ostream& (*manip)(ostream&)) {
            message << manip;
            return *this;
        }
    };
    
private:
    queue<pair<uint8_t, string>> queue_messages;
    unordered_map<uint8_t, ofstream> files;
    atomic<bool> is_work;
    thread printer;
    mutex mtx;
    condition_variable status_queue;
    
private:
    Logger()
        : is_work(true)
        , printer(&Logger::print, this) {
        auto open_file = [this](uint8_t key, string_view name) {
            auto* file = &files[key];
            
            file->open(name.data());
            
            if (file->is_open() == false) {
                files.erase(key);
            }
        };
        
        open_file(Level_log::info, "info.log");
        open_file(Level_log::debug, "debug.log");
        open_file(Level_log::warning, "warning.log");
        open_file(Level_log::error, "error.log");
    }
    
    Logger(Logger&&) = delete;
    Logger(const Logger&) = delete;
    Logger& operator=(Logger&&) = delete;
    Logger& operator=(const Logger&) = delete;
    
    ~Logger() {
        terminate();
    }
    
public:
    static Logger& get_instance() {
        static Logger instance;
        return instance;
    }

    static void terminate() {
        Logger& logger = get_instance();
        
        logger.is_work = false;
        logger.status_queue.notify_one();
        
        if (logger.printer.joinable()) {
            logger.printer.join();
        }
        
        for (auto& it : logger.files) {
            it.second.close();
        }
    }

public:
    template<class Text>
    Message operator<<(const Text& text) {
        return move(Message(Info()) << text);
    }

    Message operator<<(ostream& (*manip)(ostream&)) {
        return move(Message(Info()) << manip);
    }
    
    template<uint8_t lvl>
    Message operator<<(const Level<lvl>& id) {
        return Message(id);
    }

private:
    void print() {
        auto print_in_file = [this](uint8_t key, const string& message) {
            auto it = files.find(key);
            
            if (it != files.end()) {
                it->second << message;
            }
        };
        
        while (is_work == true || queue_messages.empty() == false) {
            unique_lock<mutex> lock(mtx);
            status_queue.wait(lock, [this] { return !queue_messages.empty() || !is_work; });
            
            while (!queue_messages.empty()) {
                auto [key, message] = move(queue_messages.front());
                queue_messages.pop();
                
                lock.unlock();
                
                cout << message;
                
                switch (key) {
                case Level_log::debug:
                case Level_log::warning:
                case Level_log::error:
                    print_in_file(key, message);
                    
                default:
                    print_in_file(Level_log::info, message);
                }
                
                lock.lock();
            }
        }
    }
    
    void add_to_queue(uint8_t level_log, string&& message) {
        lock_guard<std::mutex> lock(mtx);
        queue_messages.push({level_log, move(message)});
        status_queue.notify_one();
    }
};

Logger& logg = Logger::get_instance();

string time_now() {
    ostringstream oss;
    
    auto now = chrono::system_clock::now();
    
    time_t time = chrono::system_clock::to_time_t(now);
    tm* ltm = localtime(&time);
    oss << "[" << setfill('0') << setw(2) << ltm->tm_hour << ":" << setfill('0') << setw(2) << ltm->tm_min << ":" << setfill('0') << setw(2) << ltm->tm_sec << "]";

    return oss.str();
}

#define INFO Logger::Info()

#define DEBUG Logger::Debug() << time_now() << '[' << __FILE__ << "][" << __FUNCTION__ << "] "

#define WARNING Logger::Warning() << '[' << __FILE__ << "][" << __FUNCTION__ << "] "

#define ERROR Logger::Error() << time_now() << '[' << __FILE__ << "][" << __FUNCTION__ << "][line send message: " << __LINE__ << "] "

void read_file(string_view name) {
    ifstream file(name.data());
    
    if (file.is_open() == false) { return; }
    
    cout << endl << endl;
    
    for (size_t i = 0; i < 40; ++i) {
        cout << '-';
    }
    
    cout << endl << "this is text from file -> " << name << endl << endl;
    string text;
    while (file.eof() == false) {
        getline(file, text);
        cout << text << endl;
    }
    
    file.close();
}

int main() {
    logg << "Starting application" << endl;
    logg << INFO << "This is an info message" << endl;
    logg << DEBUG << "Debugging information" << endl;
    logg << WARNING << "This is a warning" << endl;
    logg << ERROR << "An error has occurred" << endl;
    
    Logger::terminate();
    
    read_file("info.log");
    read_file("debug.log");
    read_file("warning.log");
    read_file("error.log");
    
    return 0;
}

В моем случае я пытался сохранить привычный синтаксис std::cout. Да я знаю, что использовать define DEBUG не лучшая идея, но это просто временная реализация для тестов. пока не совсем придумал, как лучше сделать.

Информация

В рейтинге
Не участвует
Откуда
Киров (Кировская обл.), Кировская обл., Россия
Дата рождения
Зарегистрирован
Активность