Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pavlovdog/algorithms_part_2
https://github.com/pavlovdog/algorithms_part_2
algorithm graph python
Last synced: about 1 month ago
JSON representation
- Host: GitHub
- URL: https://github.com/pavlovdog/algorithms_part_2
- Owner: pavlovdog
- Created: 2016-10-22T01:03:06.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2016-10-22T20:48:44.000Z (about 8 years ago)
- Last Synced: 2024-10-12T00:34:13.171Z (3 months ago)
- Topics: algorithm, graph, python
- Language: Python
- Size: 1.57 MB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Второе домашнее задание
### Потехин Сергей, 156 группа## Структура репозитория
+ profiler - самописная утилита для тестирования производительности
+ samples - наборы данных для запуска программ
+ profiler.first.csv (profiler.second.csv) - таблица с результатами тестирования для первого (второго) пункта
+ source.first.cpp (source.second.cpp) - исходники для первого (второго) пункта
+ x.first (x.second) - исполняемые файлы для первого (второго) пункта
+ plot.first.py (plot.second.py) - скрипт для отрисовки данных из profiler.first.csv (profiler.second.csv)## Первый пункт
### Код
Используем vector used_nodes для хранения уже посещенных вершин и vector answer для хранения ребер финального паросочетания. После ввода, будем просто идти по списку ребер и если вершин из этого ребра нет в used, то добавим ребро в answer, а вершины ребра в used. Ответом будет answer.P.S. В первом и втором пункте ввод реализован через std::cin, для быстрого ввода файла можно использовать
```bash
cat data.example | ./x.first
```### Ассимптотика
Будем считать, что вершин у нас x, а ребер - y. Тогда:1. Считать x и y O(1)
2. m раз считать ребро и добавить его в vector O(m)
3. Инициализируем вектор использованных вершин O(n)
4. Идем по списку ребер O(m)
5. Проверяем начало и конец ребра на наличие в used_nodes O(1)
6. Добавляем ребро в answer, а вершины в used O(1)Итог: O(m + n)
### Доказательство
Пусть есть паросочетание, найденное нашим алгоритмом (А), и оптимальное паросочетание (Б) - сравним их. Возьмем какое-то ребро из А - оно может либо совпадать с каким-то ребром из Б (неинтересно), либо пересекаться с пересекаться к какими-то ребрами из Б.1. Ребро может пересечься с одним ребром из Б (a.б)
2. Ребро может пересечься с двумя ребрами из Б (б.а.б)Заметим, что ребро из А не может пересекаться с тремя и более ребрами из Б, тк тогда в Б присутствуют пересекающиеся ребра, что противоречит определению паросочетания. Следовательно, каждому ребру из А, можно поставить в соответствие максимум два ребра из Б, следовательно - Б больше чем А максимум в два раза.
### График зависимости времени работы (Y) от числа ребер (Х) при фиксированном числе вершин
Для графика использовался набор данных, полученных в результате тестирования командой (работает несколько минут, ~5000 тестов). Описание профайлера дано внизу страницы.
```bash
$ ./profiler --first True --nodes 100 100 --edges 10 5000
```Видно, что за исключением несольких аномальных точек, время работы зависит линейно от числа ребер. График также соответствует найденной ассимптотике.
![first](https://github.com/pavlovdog/Algorithms_part_2/blob/master/first.plot.png "First plot")
## Второй пункт
### Код
Общая идея - пройдем через список всех вершин (= строк). На каждой итерации будем смотреть, были ли мы уже в данной вершине и если нет - это значит, что мы нашли новый кластер. В этом случае увеличиваем число кластеров на 1 и запускаем BFS из этой вершины чтобы найти все оставшиеся вершины этого кластера и пометить их, как использованные.Реализация - заведем set raws для хранения вершин графа. В нашем случае вершина - это то же самое что строка. Также заведем map used_raws, где для каждой вершины будем отмечать, были ли мы уже в этой вершине. После ввода данных будем итерироваться по raws и смотреть, отмечена ли эта вершина в used_raws, как уже посещенная. Если нет - увеличиваем счетчик кластеров clusters на 1 и запускаем BFS от этой вершины. Внутри BFS мы отмечаем выбранную вершину как посещенную и начинаем искать ее соседей. Согласно условию - ее соседом будет строка, которая отличается от нашей на 1 или 2 символа. Будем искать соседей простым перебором. Будем перебирать все варианты нашей строки, измененной в одной или двух позициях сразу, и, если такая строка существует в raws и еще не посещена - запустим BFS от нее.
### Ассимптотика
1. Ввод данных. n строк, операция вставки в set и вставки в map занимает logn - O(n*logn)
2. Итерируемся по raws и проверяем на значение в map (logn)
3. Перебор всех возможных значений новых строк внутри BFS занимает O(m^2) (два вложенных цикла длиной m), генерация новой строки проиходит за O(1)
4. Проверка на вхождение новой строки в set занимает O(m)*O(logn).
5. Следовательно BFS имееет сложность O(m^3 * logn)
6. Максимум можно вызвать BFS n раз (т.к. после каждого вызова вершина будет добавляться в уже использованные).Следовательно O(n*m^3*logn)
### График зависимости времени работы (Y) от числа строк (X) при фиксированной длине строки
![second](https://github.com/pavlovdog/Algorithms_part_2/blob/master/second.plot.png "Second plot")Опять же видно, что найденная ассимптотика подходит под график.
## Профайлинг
Профайлер написан на Python 2.7, для корректной работы необходимо установить несколько библиотек.
```bash
$ sudo pip install pandas termcolor
$ ./profiler --help
usage: profiler [-h] [--first FIRST] [--second SECOND]
[--nodes NODES [NODES ...]] [--edges EDGES [EDGES ...]]
[--raws RAWS [RAWS ...]] [--lenght LENGHT [LENGHT ...]]Profiler for HW, part 2. Profiler works good right out of the box, so just
type "./profiler --first True -- second True"optional arguments:
-h, --help show this help message and exit
--first FIRST Profile first problem; default = False
--second SECOND Profile second problem; default = False
--nodes NODES [NODES ...] Nodes range for the first problem; default = 10 15
--edges EDGES [EDGES ...] Edges range for the first problem; default = 20 25
--raws RAWS [RAWS ...] Number of raws range for the second problem; default = 100 110
--lenght LENGHT [LENGHT ...] Raw's lenght for the second problem; default = 10 15
```
В принципе все неплохо работает на дефолтных настройках, достаточно запустить
```bash
$ ./profiler --first True --second True
```
Но при желании можно изменить параметры, в --help все достаточно подробно описано. Отчеты в репозитории созданы командой (работает долго, 1600 и 2000 тестов соответственно)
```bash
$ ./profiler --first True --second True --nodes 10 30 --edges 20 100 --raws 100 300 --lenght 10 20
```