diff --git a/lab 1 mid 1.cpp b/lab 1 mid 1.cpp new file mode 100644 index 00000000..e940d0f8 --- /dev/null +++ b/lab 1 mid 1.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include + +/** + * Структура для хранения одной записи (Record). + * Допустим, у неё есть: + * - ID (целое число) + * - координаты X и Y (вещественные), + * как упрощенный пример «ключевых точек». + */ +struct Node +{ + int id; + float x; + float y; + Node *left; + Node *right; + + Node(int _id, float _x, float _y) + : id(_id), x(_x), y(_y), left(nullptr), right(nullptr) {} +}; + +class CompositeIndex +{ +public: + /** + * Конструктор по умолчанию + */ + CompositeIndex() : root(nullptr) {} + + /** + * Деструктор + */ + ~CompositeIndex() + { + destroy(root); + } + + /** + * Парсим дерево из потока, используя preorder-формат, где: + * - "ID X Y" означает узел с (id, x, y) + * - "#" означает отсутствие поддерева + * + * @param iss поток данных (преобразованный из строки) + */ + void parseIndex(std::istringstream &iss) + { + // TODO + destroy(root); + root = parseNode(iss); + } + + /** + * Подсчет количества узлов + * + * @return общее количество узлов в дереве + */ + int totalNodes() const + { + // TODO + return countNodes(root); + } + + /** + * Высота дерева + * + * @return высота дерева + */ + int height() const + { + // TODO + return treeHeight(root); + } + + /** + * Количество листьев + * + * @return количество листьев в дереве + */ + int leaves() const + { + // TODO + return countLeaves(root); + } + + /** + * Проверка, что дерево удовлетворяет свойству BST: + * - Все ключи в левом поддереве < ключа в корне + * - Все ключи в правом поддереве > ключа в корне + * - Рекурсивно для всех поддеревьев + * + * @return true, если дерево корректно, иначе false + */ + bool isValid() const + { + // TODO + std::vector arr; + getInorder(root, arr); + for (size_t i = 1; i < arr.size(); ++i) + { + if (arr[i - 1] >= arr[i]) return false; + } + return true; + } + + /** + * Минимальный ID в дереве + * + * @return минимальный ID + * @throws std::runtime_error если дерево пустое + */ + int minID() const + { + // TODO + std::vector arr; + getInorder(root, arr); + if (arr.empty()) throw std::runtime_error("Дерево пустое"); + return *std::min_element(arr.begin(), arr.end()); + } + + /** + * Максимальный ID в дереве + * + * @return максимальный ID + * @throws std::runtime_error если дерево пустое + */ + int maxID() const + { + // TODO + std::vector arr; + getInorder(root, arr); + if (arr.empty()) throw std::runtime_error("Дерево пустое"); + return *std::max_element(arr.begin(), arr.end()); + } + +private: + Node *root; + + /** + * Рекурсивный парсинг (preorder): + * 1) считываем токен. Если "#", значит пустое поддерево + * 2) иначе это ID, за ним два float (x и y) + * 3) создаём узел, рекурсивно парсим левое и правое поддерево + * + * @param iss входной поток + * @return созданный узел (корень поддерева) + */ + Node *parseNode(std::istringstream &iss) + { + // TODO + std::string tok; + if (!(iss >> tok)) return nullptr; + if (tok == "#") return nullptr; + + int id = std::stoi(tok); + float x, y; + if (!(iss >> x >> y)) + throw std::runtime_error("Неверный формат ввода при разборе узла"); + + Node *node = new Node(id, x, y); + node->left = parseNode(iss); + node->right = parseNode(iss); + return node; + } + + /** + * Подсчитать количество узлов + * + * @param node корневая нода + * @return количество узлов в поддереве + */ + int countNodes(Node *node) const + { + // TODO + if (!node) return 0; + return 1 + countNodes(node->left) + countNodes(node->right); + } + + /** + * Подсчитывает высоту дерева + * + * @param node корневая нода + * @return высота поддерева + */ + int treeHeight(Node *node) const + { + // TODO + if (!node) return 0; + int lh = treeHeight(node->left); + int rh = treeHeight(node->right); + return 1 + (lh > rh ? lh : rh); + } + + /** + * Подсчитывает количество листьев + * + * @param node корневая нода + * @return количество листьев в поддереве + */ + int countLeaves(Node *node) const + { + // TODO + if (!node) return 0; + if (!node->left && !node->right) return 1; + return countLeaves(node->left) + countLeaves(node->right); + } + + /** + * Выполняет inorder-обход дерева, сохраняя ID узлов + * в переданный вектор + * + * @param node корневая нода + * @param arr вектор, куда будут сохраняться ID + */ + void getInorder(Node *node, std::vector &arr) const + { + // TODO + if (!node) return; + getInorder(node->left, arr); + arr.push_back(node->id); + getInorder(node->right, arr); + } + + /** + * Удаляет всё дерево (рекурсивно) + * + * @param node корень поддерева + */ + void destroy(Node *node) + { + // TODO + if (!node) return; + destroy(node->left); + destroy(node->right); + delete node; + } +}; + +/** + * Пример ввода: + * 10 1.0 2.0 5 0.0 0.0 # # 20 0.0 0.0 # # + * + * Структура дерева: + * 10 + * / \ + * 5 20 + */ diff --git a/lab 2 5 light.cpp b/lab 2 5 light.cpp new file mode 100644 index 00000000..f8b294f3 --- /dev/null +++ b/lab 2 5 light.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include + +/** + + Ключ: int + + Значeние: int + + Метод разрешения коллизий: свободная адресация + + Рeaлизация хeш-функций: любой + + Тип смещения: квадратичное исследование + */ +struct HashTable { + size_t m_TableSize; + std::vector>> m_Table; + + HashTable(int size) : m_TableSize(size), m_Table(size) {} + + /** + * xeш-функция + * + * @solvе + */ + int Hash(int key) const { + long long mod = static_cast(m_TableSize); + long long v = key % mod; + if (v < 0) v += mod; + return static_cast(v); + } + + /** + * Встaвка пары (ключ, значение) + * + * Если ключ ужe существует, обновляет значение + * + * @solvе + */ + void Insert(int key, int value) { + int h = Hash(key); + for (size_t i = 0; i < m_TableSize; ++i) { + int idx = static_cast((h + i * 1LL * i) % m_TableSize); + auto &bucket = m_Table[idx]; + if (!bucket.empty()) { + if (bucket.front().first == key) { + bucket.front().second = value; + return; + } + } else { + bucket.push_back({key, value}); + return; + } + } + } + + /** + * Поиск знaчения по ключу + * + * Если ключ нaйден, в value записывается найденное значение + * и вoзврaщается true, иначе false + * + * @solvе + */ + bool Get(int key, int &value) const { + int h = Hash(key); + for (size_t i = 0; i < m_TableSize; ++i) { + int idx = static_cast((h + i * 1LL * i) % m_TableSize); + const auto &bucket = m_Table[idx]; + if (!bucket.empty() && bucket.front().first == key) { + value = bucket.front().second; + return true; + } + } + return false; + } + + /** + * Удаление элемента по ключу + * + * Возвращает true, если элемент был найден и удалён, иначе false + * + * @solvе + */ + bool Remove(int key) { + int h = Hash(key); + for (size_t i = 0; i < m_TableSize; ++i) { + int idx = static_cast((h + i * 1LL * i) % m_TableSize); + auto &bucket = m_Table[idx]; + if (!bucket.empty() && bucket.front().first == key) { + bucket.clear(); + return true; + } + } + return false; + } + + std::string getTable() const { + std::string ans = ""; + for (int i = 0; i < m_TableSize; i++) { + ans += "Bucket " + std::to_string(i) + ": "; + for (const auto &entry : m_Table[i]) { + ans += "[" + std::to_string(entry.first) + ":" + std::to_string(entry.second) + "]"; + } + ans += "\n"; + } + return ans; + } + +}; + +int main() { + HashTable ht(7); + + ht.Insert(1, 10); + ht.Insert(8, 80); + ht.Insert(15, 150); + ht.Insert(2, 20); + ht.Insert(9, 90); + + int v = -1; + assert(ht.Get(1, v) && v == 10); + assert(ht.Get(8, v) && v == 80); + assert(ht.Get(15, v) && v == 150); + assert(ht.Get(2, v) && v == 20); + assert(ht.Get(9, v) && v == 90); + assert(!ht.Get(100, v)); + + ht.Insert(1, 15); + assert(ht.Get(1, v) && v == 15); + + assert(ht.Remove(8)); + assert(!ht.Get(8, v)); + + std::cout << ht.getTable() << std::endl; + + std::cout << "OK" << std::endl; + return 0; +} diff --git a/lab 3 light 1.pdf b/lab 3 light 1.pdf new file mode 100644 index 00000000..e8f1903a Binary files /dev/null and b/lab 3 light 1.pdf differ diff --git a/lab 4 edmond light.cpp b/lab 4 edmond light.cpp new file mode 100644 index 00000000..cb37db10 --- /dev/null +++ b/lab 4 edmond light.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +static const double EPS = 1e-12; + +struct Edge { + int to; + int rev; + double cap; + Edge(int t, int r, double c) : to(t), rev(r), cap(c) {} +}; + +struct FlowNetwork { + int n; + vector> adj; + + FlowNetwork(int n_) : n(n_), adj(n_) {} + + void add_edge(int u, int v, double cap) { + Edge fwd(v, (int)adj[v].size(), cap); + Edge rev(u, (int)adj[u].size(), 0.0); + adj[u].push_back(fwd); + adj[v].push_back(rev); + } + + pair edmonds_karp(int s, int t) { + double flow = 0.0; + int iterations = 0; + + while (true) { + vector parent_v(n, -1); + vector parent_e(n, -1); + queue q; + q.push(s); + parent_v[s] = s; + + while (!q.empty() && parent_v[t] == -1) { + int u = q.front(); q.pop(); + for (int ei = 0; ei < (int)adj[u].size(); ++ei) { + const Edge &e = adj[u][ei]; + if (parent_v[e.to] == -1 && e.cap > EPS) { + parent_v[e.to] = u; + parent_e[e.to] = ei; + q.push(e.to); + if (e.to == t) break; + } + } + } + + if (parent_v[t] == -1) break; + + double add = numeric_limits::infinity(); + for (int v = t; v != s; v = parent_v[v]) { + int u = parent_v[v]; + const Edge &e = adj[u][parent_e[v]]; + add = min(add, e.cap); + } + + for (int v = t; v != s; v = parent_v[v]) { + int u = parent_v[v]; + int ei = parent_e[v]; + Edge &e = adj[u][ei]; + e.cap -= add; + adj[v][e.rev].cap += add; + } + + flow += add; + iterations += 1; + } + + return {flow, iterations}; + } +}; + +static inline string trim(const string &s) { + size_t l = s.find_first_not_of(" \t\r\n"); + if (l == string::npos) return ""; + size_t r = s.find_last_not_of(" \t\r\n"); + return s.substr(l, r - l + 1); +} + +int main() { + ios::sync_with_stdio(false); + cin.tie(nullptr); + + string first; + while (true) { + if (!getline(cin, first)) { + cerr << "ошибка: пустой ввод\n"; + return 0; + } + first = trim(first); + if (!first.empty() && first[0] != '#') break; + } + + int n, m, s, t; + { + istringstream iss(first); + if (!(iss >> n >> m >> s >> t)) { + cerr << "ошибка: первая строка должна быть: n m s t (целые)\n"; + return 0; + } + } + + FlowNetwork g(n); + + int read = 0; + while (read < m) { + string line; + if (!getline(cin, line)) { + cerr << "ошибка: ожидалось ещё " << (m - read) << " строк(и) рёбер\n"; + return 0; + } + line = trim(line); + if (line.empty() || line[0] == '#') continue; + + istringstream iss(line); + int u, v; + double cap; + if (!(iss >> u >> v >> cap)) { + cerr << "ошибка в строке ребра #" << (read + 1) << ": нужно 'u v cap'\n"; + return 0; + } + if (!(0 <= u && u < n && 0 <= v && v < n)) { + cerr << "ошибка: вершины должны быть в диапазоне [0, " << (n - 1) << "]\n"; + return 0; + } + if (cap < 0) { + cerr << "ошибка: ёмкость должна быть неотрицательной\n"; + return 0; + } + g.add_edge(u, v, cap); + ++read; + } + + auto res = g.edmonds_karp(s, t); + double max_flow = res.first; + int iters = res.second; + + cout << "Вершин: " << n << " Рёбер: " << m << "\n"; + cout << "Источник: " << s << " Сток: " << t << "\n"; + cout.setf(std::ios::fmtflags(0), std::ios::floatfield); + cout << setprecision(10); + cout << "Максимальный поток: " << max_flow << "\n"; + cout << "Итераций усиления: " << iters << "\n"; + + return 0; +} + +/* +Пример ввода: +4 5 0 3 +0 1 5 +0 2 3 +1 2 2 +1 3 3 +2 3 4 +*/ diff --git a/lab 5 mid 4.py b/lab 5 mid 4.py new file mode 100644 index 00000000..ff17149c --- /dev/null +++ b/lab 5 mid 4.py @@ -0,0 +1,373 @@ +# ============================================================================= +# ФАЙЛ: entrypoint/main.py +# ЯЗЫК/СТЕК: CPython + numpy + Pillow. (Без scipy, без opencv.) +# +# НАЗНАЧЕНИЕ МОДУЛЯ +# Найти яблоко на изображении apple.png по простому цветовому правилу, +# оставить крупнейшую связную компоненту как GT-маску, построить +# накрывающий её эллипс (по ковариации), сравнить их метрикой IoU и +# сохранить визуализации. +# +# КЛАССЫ +# В этом файле КЛАССОВ НЕТ. Работа организована как набор простых функций. +# +# ИДЕЯ РЕШЕНИЯ +# 1) Сегментация: перевод в HSV + фильтр «красных» оттенков с порогами по +# насыщенности/яркости. В качестве страховки — проверка «красноты» в RGB. +# Далее берём крупнейшую связную компоненту (4-соседство). +# 2) Эллипс: считаем центр масс маски, ковариацию 2×2, берём её +# собственные пары (направления/дисперсии). Находим максимальное +# Махаланобисово расстояние d^2 до точек маски и растягиваем полуоси: +# a = sqrt(λ_max * max_d2), b = sqrt(λ_min * max_d2). +# 3) IoU: сравниваем бинарную GT-маску и маску эллипса. +# 4) Визуализация: рисуем контур эллипса поверх исходного изображения. +# + +import os +import math +import numpy as np +from typing import Tuple, List +from PIL import Image, ImageDraw + + +def _load_image() -> Image.Image: + """ + Назначение: + Загрузить исходное изображение apple.png (RGB). + + Идея: + Ищем файл в нескольких «кандидатах» рядом с entrypoint/ и из cwd. + + Реализация: + Перебираем пути и возвращаем Pillow-изображение в режиме RGB. + Если файл не найден — бросаем FileNotFoundError. + + Возвращает: + PIL.Image.Image — загруженная картинка в RGB. + """ + # Пытаемся найти apple.png рядом с репо и из cwd + here = os.path.dirname(__file__) + cand = [ + os.path.join(here, "..", "apple.png"), + os.path.join(here, "apple.png"), + "apple.png", + ] + for p in cand: + if os.path.exists(p): + return Image.open(p).convert("RGB") + raise FileNotFoundError("apple.png не найден (ожидалось рядом с entrypoint/).") + + +def _segment_apple(img: Image.Image) -> np.ndarray: + """ + Назначение: + Построить бинарную маску яблока по цвету. + + Идея: + Красные оттенки детектируем в HSV по диапазону hue и порогам S/V, + а также страхуемся простым правилом «R существенно больше G и B» в RGB. + После этого оставляем только крупнейшую связную компоненту. + + Реализация: + - Конвертируем в HSV через Pillow, считаем H∈[0,360), S,V∈[0,1]. + - Формируем две маски (HSV и RGB), объединяем логическим ИЛИ. + - Выделяем крупнейшую компоненту функцией _largest_component. + + Аргументы: + img : PIL.Image.Image — входное изображение RGB. + + Возвращает: + np.ndarray (bool, H×W) — бинарная маска яблока. + """ + arr = np.asarray(img) + HxW = arr.shape[:2] # не используется далее, но остаётся как подсказка формы + + # HSV через Pillow + hsv = img.convert("HSV") + hsv_arr = np.asarray(hsv).astype(np.float32) + H = hsv_arr[..., 0] * (360.0 / 255.0) + S = hsv_arr[..., 1] / 255.0 + V = hsv_arr[..., 2] / 255.0 + + # "красные" тона + насыщенность/яркость + mask_hsv = (((H < 25.0) | (H > 335.0)) & (S > 0.35) & (V > 0.20)) + + # страховка по "красноте" в RGB + R = arr[..., 0].astype(np.float32) + G = arr[..., 1].astype(np.float32) + B = arr[..., 2].astype(np.float32) + mask_rgb = (R > 1.15 * G) & (R > 1.15 * B) & (R > 60.0) + + M = (mask_hsv | mask_rgb) + + # берём крупнейшую связную компоненту (4-соседство) + M = _largest_component(M) + return M + + +def _largest_component(M: np.ndarray) -> np.ndarray: + """ + Назначение: + Оставить только крупнейшую связную компоненту True-пикселей. + + Идея: + Прямолинейный обход всех стартовых точек и итеративный DFS со + стеком по 4-соседям. + + Реализация: + - Если маска пустая — возвращаем её как есть. + - Для каждого непросмотренного True запускаем DFS, накапливаем + список координат компоненты и помним максимальную по размеру. + + Аргументы: + M : np.ndarray (bool, H×W) — исходная маска. + + Возвращает: + np.ndarray (bool, H×W) — маска только с наибольшей компонентой. + """ + h, w = M.shape + vis = np.zeros_like(M, dtype=bool) + best_cnt, best_coords = 0, None + + # быстрый выход, если маска пустая + if not M.any(): + return M + + # итеративный DFS (стек) + for y in range(h): + row = M[y] + for x in range(w): + if row[x] and not vis[y, x]: + stack: List[Tuple[int, int]] = [(y, x)] + vis[y, x] = True + coords: List[Tuple[int, int]] = [(y, x)] + while stack: + cy, cx = stack.pop() + for dy, dx in ((1, 0), (-1, 0), (0, 1), (0, -1)): + ny, nx = cy + dy, cx + dx + if 0 <= ny < h and 0 <= nx < w and M[ny, nx] and not vis[ny, nx]: + vis[ny, nx] = True + stack.append((ny, nx)) + coords.append((ny, nx)) + if len(coords) > best_cnt: + best_cnt, best_coords = len(coords), coords + + out = np.zeros_like(M, dtype=bool) + if best_coords: + ys, xs = zip(*best_coords) + out[np.array(ys), np.array(xs)] = True + return out + + +def _ellipse_from_mask(M: np.ndarray) -> Tuple[float, float, float, float, float]: + """ + Назначение: + Построить повёрнутый эллипс, накрывающий маску. + + Идея: + Центр эллипса — центр масс маски. Направления осей — собственные + векторы ковариации координат (principal components, 2D). + Радиусы (полуоси) получаем из собственных значений и максимального + Махаланобисова расстояния до точек маски. + + Реализация: + - Собираем X = [[x - cx], [y - cy]] (размер 2×N), считаем cov = XX^T / N. + - Находим Σ^{-1}, считаем d^2_i = (x_i - μ)^T Σ^{-1} (x_i - μ). + - Пусть max_d2 = max_i d^2_i. Тогда a = sqrt(λ_max * max_d2), + b = sqrt(λ_min * max_d2), угол — аргумент собственного вектора + при λ_max. + + Аргументы: + M : np.ndarray (bool, H×W) — бинарная маска. + + Возвращает: + (cx, cy, a, b, angle_rad): + cx, cy — центр, + a, b — полуоси (a ≥ b по построению), + angle — угол поворота в радианах (ось a относительно оси X). + """ + ys, xs = np.nonzero(M) + if len(xs) == 0: + raise RuntimeError("Пустая маска — не из чего строить эллипс.") + + cx = xs.mean() + cy = ys.mean() + + X = np.stack([xs - cx, ys - cy], axis=0) # 2xN + # ковариация 2x2 + cov = (X @ X.T) / X.shape[1] + cov += np.eye(2) * 1e-6 # регуляризация от вырождения + inv_cov = np.linalg.inv(cov) + + # max d^2 по точкам маски + # d^2 = (x-μ)^T Σ^{-1} (x-μ) + d2 = np.sum((X.T @ inv_cov) * X.T, axis=1) + max_d2 = float(d2.max()) + + # собственные значения/векторы для полуосей и угла + evals, evecs = np.linalg.eigh(cov) + order = np.argsort(evals)[::-1] + evals = evals[order] + evecs = evecs[:, order] + + a = math.sqrt(evals[0] * max_d2) # большая полуось + b = math.sqrt(evals[1] * max_d2) # малая полуось + vx, vy = evecs[:, 0] + angle = math.atan2(vy, vx) + + return cx, cy, a, b, angle + + +def _ellipse_mask( + shape: Tuple[int, int], + cx: float, + cy: float, + a: float, + b: float, + angle: float +) -> np.ndarray: + """ + Назначение: + Синтезировать булеву маску пикселей, попадающих внутрь заданного эллипса. + + Идея: + Для каждой пиксельной координаты применяем поворот в систему эллипса + и проверяем стандартное уравнение: (x/a)^2 + (y/b)^2 ≤ 1. + + Реализация: + - Формируем сетку координат yy, xx. + - Вычитаем центр, поворачиваем на -angle. + - Считаем d^2 и сравниваем с 1. + + Аргументы: + shape : (H, W) — форма выходной маски, + cx,cy : float — центр эллипса, + a,b : float — полуоси, + angle : float — угол поворота (рад). + + Возвращает: + np.ndarray (bool, H×W) — маска эллипса. + """ + h, w = shape + yy, xx = np.mgrid[0:h, 0:w] + # поворот координат: применим формулу d^2 <= 1 в системе эллипса + cos_t = math.cos(angle) + sin_t = math.sin(angle) + x = xx - cx + y = yy - cy + xr = cos_t * x + sin_t * y + yr = -sin_t * x + cos_t * y + d2 = (xr * xr) / (a * a + 1e-12) + (yr * yr) / (b * b + 1e-12) + return d2 <= 1.0 + + +def _draw_ellipse( + img: Image.Image, + cx: float, + cy: float, + a: float, + b: float, + angle: float, + color=(255, 0, 0), + width=3 +) -> Image.Image: + """ + Назначение: + Нарисовать повёрнутый эллипс на изображении (контуром). + + Идея: + Сэмплируем параметры t∈[0,2π) равномерно и строим полилинию + по точкам параметрического эллипса, повернутого на angle. + + Реализация: + - Генерируем steps точек (x=a cos t, y=b sin t). + - Поворачиваем и сдвигаем к (cx,cy). + - Проводим линию через все точки и замыкаем её. + + Аргументы: + img : PIL.Image.Image — картинка для рисования, + cx,cy : float — центр, + a,b : float — полуоси, + angle : float — угол поворота (рад), + color : tuple — цвет контура, + width : int — толщина линии. + + Возвращает: + PIL.Image.Image — изображение с нарисованным эллипсом. + """ + draw = ImageDraw.Draw(img) + pts = [] + steps = 360 + cos_t, sin_t = math.cos(angle), math.sin(angle) + for t in np.linspace(0, 2 * math.pi, steps, endpoint=False): + x = a * math.cos(t) + y = b * math.sin(t) + xr = x * cos_t - y * sin_t + yr = x * sin_t + y * cos_t + pts.append((cx + xr, cy + yr)) + draw.line(pts + [pts[0]], fill=color, width=width) + return img + + +def _iou(mask_a: np.ndarray, mask_b: np.ndarray) -> float: + """ + Назначение: + Посчитать IoU (Jaccard) двух бинарных масок. + + Идея: + IoU = |A∩B| / |A∪B|. Если объединение пусто — вернуть 0. + + Реализация: + Используем логические операции numpy и суммируем True как int64. + + Аргументы: + mask_a, mask_b : np.ndarray(bool, H×W) + + Возвращает: + float — значение IoU в [0,1]. + """ + inter = np.logical_and(mask_a, mask_b).sum(dtype=np.int64) + union = np.logical_or(mask_a, mask_b).sum(dtype=np.int64) + return float(inter) / float(union) if union else 0.0 + + +def main() -> None: + """ + Назначение: + Склеить весь конвейер: загрузка → сегментация → эллипс → + сравнение → визуализация и сохранение результатов. + + Реализация: + - Читаем изображение. + - Строим GT-маску яблока (_segment_apple). + - Строим накрывающий эллипс (_ellipse_from_mask). + - Синтезируем маску эллипса и считаем IoU. + - Рисуем эллипс поверх исходника и сохраняем два PNG: + apple_ellipse.png (контур) и apple_mask.png (GT-маска). + - Печатаем IoU и пути сохранения. + """ + img = _load_image() + M = _segment_apple(img) # GT-маска яблока по цвету + largest CC + cx, cy, a, b, angle = _ellipse_from_mask(M) + Emask = _ellipse_mask(M.shape, cx, cy, a, b, angle) + iou = _iou(M, Emask) + + # Сохраняем визуализацию + vis = img.copy() + vis = _draw_ellipse(vis, cx, cy, a, b, angle, color=(255, 0, 0), width=3) + + out_dir = os.getcwd() + vis_path = os.path.join(out_dir, "apple_ellipse.png") + mask_vis = Image.fromarray((M * 255).astype(np.uint8)) + mask_path = os.path.join(out_dir, "apple_mask.png") + + vis.save(vis_path) + mask_vis.save(mask_path) + + print(f"IoU(ellipse vs mask) = {iou:.6f}") + print(f"Saved: {vis_path}") + print(f"Saved: {mask_path}") + + +if __name__ == "__main__": + main() diff --git "a/lab 7 \320\261\320\270\321\202\320\262\320\260 \320\272\320\273\320\260\320\275\320\276\320\262.cpp" "b/lab 7 \320\261\320\270\321\202\320\262\320\260 \320\272\320\273\320\260\320\275\320\276\320\262.cpp" new file mode 100644 index 00000000..2ce5c80a --- /dev/null +++ "b/lab 7 \320\261\320\270\321\202\320\262\320\260 \320\272\320\273\320\260\320\275\320\276\320\262.cpp" @@ -0,0 +1,71 @@ +// "Войны кланов": +#include +#include + +class ClansDSU { +public: + explicit ClansDSU(int n) + : parent(n), sz(n, 1), members(n) + { + for (int i = 0; i < n; ++i) { + parent[i] = i; + members[i].push_back(i); // для опционального disband/members() + } + } + + // Найти представителя клана пользователя u + int find(int u) { + if (parent[u] == u) return u; + return parent[u] = find(parent[u]); // компрессия пути + } + + // В одном ли клане два пользователя + bool same(int u, int v) { return find(u) == find(v); } + + // Объединить кланы пользователей u и v, вернуть новый корень + int unite(int u, int v) { + int a = find(u), b = find(v); + if (a == b) return a; + if (sz[a] < sz[b]) std::swap(a, b); // a — больший + // привязываем b к a + parent[b] = a; + sz[a] += sz[b]; + + // --- поддержка списков участников (опционально) --- + // small-to-large: переносим меньший список в больший — суммарно O(n log n) + if (members[b].size() > members[a].size()) std::swap(members[a], members[b]); + for (int x : members[b]) { + parent[x] = a; // ускоряет дальнейшие find до компрессии + members[a].push_back(x); + } + members[b].clear(); + // --------------------------------------------------- + return a; + } + + // Размер клана пользователя u + int clan_size(int u) { return sz[find(u)]; } + + // Список участников клана пользователя u (опционально; валиден для чтения) + const std::vector& clan_members(int u) { return members[find(u)]; } + + // Опционально: распустить клан пользователя u (каждый участник снова сам по себе) + // Сложность O(|clan|) + void disband_by_user(int u) { + int r = find(u); + auto list = members[r]; // копия, чтобы безопасно итерироваться + for (int x : list) { + parent[x] = x; + sz[x] = 1; + members[x].clear(); + members[x].push_back(x); + } + members[r].clear(); + sz[r] = 1; // r сам стал синглтоном (уже настроен в цикле, строка не критична) + } + +private: + std::vector parent; + std::vector sz; + std::vector> members; // хранится только у корней; у не-корней — пусто +}; diff --git a/lab 8 mid 1.cpp b/lab 8 mid 1.cpp new file mode 100644 index 00000000..ba922a1f --- /dev/null +++ b/lab 8 mid 1.cpp @@ -0,0 +1,149 @@ +// mm_bench.cpp +// Наивное и многопоточное умножение матриц C = A * B. +// Разбиение по строкам C, заранее строим BT = B^T. +// Размеры: 32, 128, 512, 1024 (по 10 запусков каждый). +// Аргументы: [threads] [verify_small](0/1) +// threads — кол-во потоков (по умолчанию hardware_concurrency()). +// verify_small — сверка результатов на размере 32 (по умолчанию 0). + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +using Clock = chrono::high_resolution_clock; + +static inline void mult_serial( + const double* A, const double* BT, double* C, int n) +{ + for (int i = 0; i < n; ++i) { + const double* Ai = A + size_t(i) * n; + double* Ci = C + size_t(i) * n; + for (int j = 0; j < n; ++j) { + const double* BTj = BT + size_t(j) * n; + double s = 0.0; + for (int k = 0; k < n; ++k) { + s += Ai[k] * BTj[k]; + } + Ci[j] = s; + } + } +} + +struct Range { int beg, end; }; + +static inline void mult_range( + const double* A, const double* BT, double* C, int n, Range r) +{ + for (int i = r.beg; i < r.end; ++i) { + const double* Ai = A + size_t(i) * n; + double* Ci = C + size_t(i) * n; + for (int j = 0; j < n; ++j) { + const double* BTj = BT + size_t(j) * n; + double s = 0.0; + for (int k = 0; k < n; ++k) { + s += Ai[k] * BTj[k]; + } + Ci[j] = s; + } + } +} + +static inline void mult_parallel( + const double* A, const double* BT, double* C, int n, unsigned threads) +{ + if (threads <= 1) { + mult_serial(A, BT, C, n); + return; + } + vector pool; + pool.reserve(threads); + int chunk = (n + int(threads) - 1) / int(threads); + for (unsigned t = 0; t < threads; ++t) { + int beg = int(t) * chunk; + int end = min(n, beg + chunk); + if (beg >= end) break; + pool.emplace_back([=]() { + mult_range(A, BT, C, n, {beg, end}); + }); + } + for (auto& th : pool) th.join(); +} + +static inline void transpose(const double* B, double* BT, int n) +{ + for (int i = 0; i < n; ++i) { + const double* Bi = B + size_t(i) * n; + for (int j = 0; j < n; ++j) { + BT[j * size_t(n) + i] = Bi[j]; + } + } +} + +static double linf_diff(const double* X, const double* Y, int n) +{ + double m = 0.0; + size_t N = size_t(n) * n; + for (size_t i = 0; i < N; ++i) m = max(m, abs(X[i] - Y[i])); + return m; +} + +int main(int argc, char** argv) +{ + unsigned threads = thread::hardware_concurrency(); + bool verify_small = false; + if (argc >= 2) threads = max(1u, (unsigned)stoi(argv[1])); + if (argc >= 3) verify_small = (stoi(argv[2]) != 0); + + vector sizes = {32, 128, 512, 1024}; + const int iters = 10; + + mt19937_64 rng(42); + uniform_real_distribution dist(-1.0, 1.0); + + cout << "#threads=" << threads << "\n"; + cout << "size,mode,iter,time_ms\n"; + + for (int n : sizes) { + size_t N = size_t(n) * n; + vector A(N), B(N), BT(N), C(N), Cpar(N); + + for (size_t i = 0; i < N; ++i) { + A[i] = dist(rng); + B[i] = dist(rng); + } + transpose(B.data(), BT.data(), n); + + // --- serial --- + for (int it = 0; it < iters; ++it) { + fill(C.begin(), C.end(), 0.0); + auto t0 = Clock::now(); + mult_serial(A.data(), BT.data(), C.data(), n); + auto t1 = Clock::now(); + double ms = chrono::duration(t1 - t0).count(); + cout << n << ",serial," << it << "," << ms << "\n"; + } + + // --- parallel --- + for (int it = 0; it < iters; ++it) { + fill(Cpar.begin(), Cpar.end(), 0.0); + auto t0 = Clock::now(); + mult_parallel(A.data(), BT.data(), Cpar.data(), n, threads); + auto t1 = Clock::now(); + double ms = chrono::duration(t1 - t0).count(); + cout << n << ",parallel," << it << "," << ms << "\n"; + } + + if (verify_small && n == 32) { + double err = linf_diff(C.data(), Cpar.data(), n); + cerr << "[verify 32x32] L_inf diff = " << err << "\n"; + } + } + return 0; +} diff --git a/lab 9 mid 1.py b/lab 9 mid 1.py new file mode 100644 index 00000000..83a7ad36 --- /dev/null +++ b/lab 9 mid 1.py @@ -0,0 +1,155 @@ + +import sys +from typing import List, Tuple + +EPS = 1e-9 + +class Simplex: + def __init__(self, A: List[List[float]], b: List[float], c: List[float]): + self.m = len(A) + self.n = len(c) + cols = self.n + self.m + 1 + self.a = [[0.0]*cols for _ in range(self.m+1)] + for i in range(self.m): + for j in range(self.n): + self.a[i][j] = A[i][j] + self.a[i][self.n + i] = 1.0 # slack + self.a[i][self.n + self.m] = b[i] + for j in range(self.n): + self.a[self.m][j] = -c[j] # maximize + self.basis = [self.n + i for i in range(self.m)] + + def _pivot_col(self) -> int: + q = -1 + best = EPS + last = self.a[self.m] + for j in range(len(last)-1): + if last[j] > best: + best = last[j] + q = j + return q + + def _pivot_row(self, q: int) -> int: + p = -1 + best = 0.0 + for i in range(self.m): + aj = self.a[i][q] + if aj > EPS: + ratio = self.a[i][self.n + self.m] / aj + if p == -1 or ratio < best - 1e-15 or (abs(ratio-best) <= 1e-15 and self.basis[i] > self.basis[p]): + best = ratio + p = i + return p + + def _do_pivot(self, p: int, q: int): + div = self.a[p][q] + self.a[p] = [v/div for v in self.a[p]] + for i in range(self.m+1): + if i == p: continue + factor = self.a[i][q] + if abs(factor) > EPS: + self.a[i] = [self.a[i][j] - factor*self.a[p][j] for j in range(len(self.a[i]))] + self.basis[p] = q + + def solve(self) -> Tuple[float, List[float]]: + while True: + q = self._pivot_col() + if q == -1: + break + p = self._pivot_row(q) + if p == -1: + raise RuntimeError("LP is unbounded") + self._do_pivot(p, q) + x = [0.0]*self.n + for i in range(self.m): + if self.basis[i] < self.n: + x[self.basis[i]] = self.a[i][self.n + self.m] + opt = self.a[self.m][self.n + self.m] + return opt, x + +def solve_baryga(T: int, B: float): + items = [ + ("ежедневник", 150.0, 400.0, 10.0, 0.15), + ("нить", 50.0, 150.0, 20.0, 0.01), + ("носки", 30.0, 100.0, 5.0, 0.05), + ] + I = len(items) + alpha = [30.0/(it[3] + 30.0) for it in items] + + def idx_x(i,t): return i*T + t + def idx_s(i,t): return I*T + i*T + t + + n_vars = 2*I*T + A: List[List[float]] = [] + b: List[float] = [] + c = [0.0]*n_vars + + + for i,(name,cost,price,tau,ret) in enumerate(items): + for t in range(T): + c[idx_s(i,t)] = price*(1.0-ret) + c[idx_x(i,t)] = -cost + + + for i,(name,cost,price,tau,ret) in enumerate(items): + for t in range(T): + row = [0.0]*n_vars + row[idx_s(i,t)] += 1.0 + for k in range(t+1): + row[idx_x(i,k)] += -alpha[i] + for k in range(t): + row[idx_s(i,k)] += alpha[i]*(1.0 - ret) + A.append(row) + b.append(0.0) + + + for t in range(T): + row = [0.0]*n_vars + for i,(name,cost,price,tau,ret) in enumerate(items): + row[idx_x(i,t)] = cost + A.append(row) + b.append(B) + + opt, x = Simplex(A,b,c).solve() + + # unpack + X = [[0.0]*T for _ in range(I)] + S = [[0.0]*T for _ in range(I)] + for i in range(I): + for t in range(T): + X[i][t] = x[idx_x(i,t)] + S[i][t] = x[idx_s(i,t)] + + end_inv = [] + for i,(name,cost,price,tau,ret) in enumerate(items): + bought = sum(X[i]) + sold_net = (1.0 - ret)*sum(S[i]) + end_inv.append(max(0.0, bought - sold_net)) + + return items, alpha, opt, X, S, end_inv + +def main(): + B = 100000.0 + if len(sys.argv) >= 2: + B = float(sys.argv[1]) + print(f"Бюджет на закупку в месяц: {B:.2f}\n") + + for T in (3,6,12): + items, alpha, opt, X, S, end_inv = solve_baryga(T, B) + print(f"=== Горизонт T={T} мес. ===") + print(f"Оптимальная прибыль: {opt:.2f}\n") + for i,(name,cost,price,tau,ret) in enumerate(items): + print(f"Товар: {name}") + print(f" alpha={alpha[i]:.6f}, возвраты={ret*100:.2f}%") + print(" Закупки:", " ".join(f"{v:.2f}" for v in X[i])) + print(" Продажи:", " ".join(f"{v:.2f}" for v in S[i])) + print(f" Остаток к концу горизонта: {end_inv[i]:.2f}\n") + # фактические траты по месяцам + print("Фактические траты по месяцам:") + for t in range(T): + spend = sum(items[i][1]*X[i][t] for i in range(len(items))) + print(f" Месяц {t+1}: {spend:.2f} / {B:.2f}") + print() + +if __name__ == "__main__": + main() diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 1.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 1.pdf" new file mode 100644 index 00000000..7a19e7de Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 1.pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 2.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 2.pdf" new file mode 100644 index 00000000..35634609 Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 2.pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 4.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 4.pdf" new file mode 100644 index 00000000..5f11aaf7 Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 4.pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 5.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 5.pdf" new file mode 100644 index 00000000..627bac5b Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 5.pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 7 (2).pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 7 (2).pdf" new file mode 100644 index 00000000..f1b30b94 Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 7 (2).pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 8.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 8.pdf" new file mode 100644 index 00000000..8bed532c Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 8.pdf" differ diff --git "a/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 9.pdf" "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 9.pdf" new file mode 100644 index 00000000..4332d606 Binary files /dev/null and "b/\320\233\320\260\320\261\320\276\321\200\320\260\321\202\320\276\321\200\320\275\320\260\321\217 9.pdf" differ