Search
Write a publication
Pull to refresh
3
0
Александр @Acaunt

User

Send message

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 не лучшая идея, но это просто временная реализация для тестов. пока не совсем придумал, как лучше сделать.

Information

Rating
Does not participate
Location
Киров (Кировская обл.), Кировская обл., Россия
Date of birth
Registered
Activity