Участник:Sveta: различия между версиями
Sveta (обсуждение | вклад) (Новая страница: « = Свойства и структура алгоритмов = == Общее описание алгоритма == Модель дуополии Штаке…») |
Sveta (обсуждение | вклад) |
||
(не показана 61 промежуточная версия этого же участника) | |||
Строка 1: | Строка 1: | ||
+ | Основные авторы описания: [[Участник:Sveta|Закирова С.В.]] | ||
+ | Поиск равновесия в дуополии Штакельберга | ||
+ | == Свойства и структура алгоритмов == | ||
− | = | + | === Общее описание алгоритма === |
+ | Модель дуополии Штакельберга является развитием модели дуополии Курно. Если в модели Курно считается, что участники рынка не прогнозируют отклика конкурента на собственные действия, то в модели Штакельберга один участник рынка не прогнозирует поведения конкурента, а второй учитывает поведение первого, зная, что конкурент не ответит на его действия. Другими словами, второй участник рынка знает, что первый участник рынка ведет себя в соответствии с моделью Курно. <ref> Шагин, В. Л. Теория игр с экономическими приложениями. Учебное пособие. — М., ГУ-ВШЭ, 2003. </ref> | ||
+ | === Математическое описание алгоритма === | ||
+ | Есть множество стратегий первого игрока <math> X </math> и множество стратегий <math> Y </math> второго игрока. | ||
+ | Первым ходит игрок, называемый лидером, его стратегия <math> x \in X </math>. Второй игрок, называемый подчиненным, ходит стратегией <math> y \in Y </math>. У каждого игрока есть своя функция выигрыша. Для лидера это функция <math> H(x,y) </math>. | ||
+ | Для подчиненного <math> G(x,y) </math>. Оба хотят максимизировать свой выигрыш, при условии лояльности(лояльность, это когда при условии одинакового выигрыша для второго, он максимизирует выигрыш первого) второго игрока по отношении к первому. Набор стратегий <math> (x^*,y^*) </math> называется равновесием Штакельберга<ref> Васин А.А., Морозов В.В. "Введение в теорию игр с приложениями в экономике"(учебное пособие). - М.: 2003. - 278 с. Pages 131-132 </ref>, если <math> y^* = R(x^*) </math> есть наилучший ответ подчиненного на стратегию лидера, которая находится как решение задачи <math> H(x^*, y^*) = max H(x,R(x)) </math> (максимум берется по всем стратегиям <math>x</math> из <math>X</math>) | ||
− | == | + | === Вычислительное ядро алгоритма === |
+ | Вычислительным ядром данного алгоритма является поиск максимума в строке. Таким образом мы находим наилучший ответ второго игрока. | ||
− | + | === Макроструктура алгоритма === | |
+ | Как уже сказано в пункте выше, основнуая составляющая часть алгоритма это поиск максимума в строке. | ||
− | + | Алгоритм можно представить как многократное повторение(по количеству строк) поиска максимума в строках первой матрицы. После этого поиск максимума по элементам второй матрицы, индексы которых уже были найдены в первой итерации. | |
− | == | + | === Схема реализации последовательного алгоритма === |
+ | <source lang="C++"> | ||
+ | #include <vector> | ||
+ | #include <cstdlib> | ||
+ | #include <ctime> | ||
+ | using namespace std; | ||
− | == | + | int main(int argc, char *argv[]) |
+ | { | ||
+ | //считиываем m и n | ||
+ | int n = strtol(argv[1], NULL, 10); | ||
+ | int m = strtol(argv[2], NULL, 10); | ||
+ | |||
+ | //создаем матрицы G и H | ||
+ | vector<vector<double>> H(n, vector<double>(m)); | ||
+ | vector<vector<double>> G(n, vector<double>(m)); | ||
+ | |||
+ | //инициализируем матрицы G и H | ||
+ | srand(time(NULL)); | ||
+ | for(int i = 0; i < n; ++i) { | ||
+ | for(int j = 0; j < m; ++j) { | ||
+ | H[i][j] = rand(); | ||
+ | G[i][j] = rand(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //ищем максимум в G благожелательно к лидеру на каждую его стратегию | ||
+ | vector<int> ind(n); | ||
+ | for(int i = 0; i < n; ++i){ | ||
+ | double max_in_line = H[i][0]; | ||
+ | int ind_max = 0; | ||
+ | for(int j = 0; j < m; ++j){ | ||
+ | if (G[i][j] > max_in_line) { | ||
+ | max_in_line = G[i][j]; | ||
+ | ind_max = j; | ||
+ | } else if (G[i][j] == max_in_line) { | ||
+ | if (H[i][j] > H[i][ind_max]) { | ||
+ | ind_max = j; | ||
+ | max_in_line = G[i][j]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | ind[i] = ind_max; | ||
+ | } | ||
+ | |||
+ | //ищем с учетом ответа второго наилучшую тратегию для лидера | ||
+ | int str_h = 0; | ||
+ | for(int i = 0; i < n; ++i) { | ||
+ | if (H[i][ind[i]] > H[str_h][ind[str_h]]) { | ||
+ | str_h = i; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | </source> | ||
+ | |||
+ | === Последовательная сложность алгоритма === | ||
+ | Для первой части - поиска наилучших стратегий для воторого потребуется <math> m * n </math> операций сравенения. | ||
+ | |||
+ | Для второй части - поиска наилучших стратегий первого потребуется <math> n </math> операций сравнения. | ||
+ | |||
+ | Таким образом результирующая сложность алгоритма - <math> О(n * m) </math> операций. | ||
+ | |||
+ | === Информационный граф === | ||
+ | Опишем граф алгоритма в виде рисунка. | ||
+ | [[File:Информационный граф РШ.png]] | ||
+ | |||
+ | Круглые элеменеты графа - элементы матрицы G. Под max, имеется в виду функция поиска максимума в строке (причем с уметом благожелательности). | ||
+ | |||
+ | max2 - опять же функция поиска максимума для первого, исходя из ответов второго. | ||
+ | |||
+ | === Ресурс параллелизма алгоритма === | ||
+ | Каждую строку можно обрабатывать отдельным процессором. Таким образом каждый процессор бежит по строке и ищет там максимум (с условием благосклонности к лидеру). После этого процессоры синхронизируются и мы ищем максимум из уже найденых значений, таким образом мы получаем наибольший выигрыш для лидера, после чего находим все равновесия по Штакельбергу. | ||
+ | |||
+ | Параллельная сложность: <math>О(m)</math>, где <math>m</math> число столбцов а нашей матрице. | ||
+ | |||
+ | === Входные и выходные данные алгоритма === | ||
+ | Входными данными являются пара матриц <math> H(x,y) </math>, <math> G(x,y) </math> одинакового размера <math>n * m </math> (стратегии обоих игроков). Выходные данные это множество пар чисел <math> (x^*,y^*) </math>, где каждая из пар является равновесием по Штакельбергу в нашей дуаполии. | ||
+ | |||
+ | == Программная реализация алгоритма == | ||
+ | |||
+ | === Особенности реализации последовательного алгоритма === | ||
+ | |||
+ | === Локальность данных и вычислений === | ||
+ | |||
+ | === Возможные способы и особенности параллельной реализации алгоритма === | ||
+ | |||
+ | === Масштабируемость алгоритма и его реализации === | ||
+ | Проведём исследование масштабируемости параллельной реализации алгоритма. Набор и границы значений изменяемых параметров запуска реализации алгоритма: | ||
+ | |||
+ | число процессоров [1,2 4, 8 : 32] со значениями квадрата целого числа; | ||
+ | |||
+ | размер матрицы(для тестов бралась квадратная матрица) [1000:10000] с шагом 1000. | ||
+ | |||
+ | На следующем рисунке приведен график производительности выбранной реализации алгоритма в зависимости от изменяемых параметров запуска. | ||
+ | [[Файл:График_РШ.png |thumb|center|700px|Рис.1. График маштабируемости]] | ||
+ | |||
+ | '''сведения о программно-аппаратной среде''' | ||
+ | |||
+ | компьютер: суперкомпьютор Ломоносов | ||
+ | |||
+ | компилятор с использованными опциями оптимизации: mpicxx версии openmpi/1.8.4-gcc | ||
+ | |||
+ | библиотеки: #include <mpi.h>, #include <iostream>, #include <cstdlib>, #include <ctime>, #include <vector> | ||
+ | |||
+ | '''выводы о масштабируемости''' | ||
+ | |||
+ | Мы получили ожидаемые результаты(в целом), что производительность увеличивается с | ||
+ | увеличеснием количества процессоров и уменьшается с увеличеннием количества строк. | ||
+ | Это можно объяснить тем, что выбранный алгоритм очень хорошо распараллеливается, а | ||
+ | «общение» процессоров почти не требуется (это важно так как эта процедура достаточно | ||
+ | ресурсозатратна). | ||
+ | |||
+ | === Динамические характеристики и эффективность реализации алгоритма === | ||
+ | |||
+ | === Выводы для классов архитектур === | ||
+ | |||
+ | |||
+ | |||
+ | === Существующие реализации алгоритма === | ||
+ | Написанная мной, текущая реализация алгоритма: | ||
+ | <source lang="C++"> | ||
+ | |||
+ | #include <mpi.h> | ||
+ | #include <iostream> | ||
+ | #include <cstdlib> | ||
+ | #include <ctime> | ||
+ | #include <vector> | ||
+ | |||
+ | using namespace std; | ||
+ | |||
+ | |||
+ | |||
+ | int main(int argc, char * argv[]) | ||
+ | { | ||
+ | |||
+ | int comm_sz, my_rank; | ||
+ | MPI_Init(&argc, &argv); | ||
+ | MPI_Comm comm = MPI_COMM_WORLD; | ||
+ | MPI_Comm_size(comm, &comm_sz); | ||
+ | MPI_Comm_rank(comm, &my_rank); | ||
+ | |||
+ | //считываем размеры матрицы | ||
+ | int n,m; | ||
+ | if (my_rank == 0) { | ||
+ | n = strtol(argv[1], NULL, 10); | ||
+ | m = strtol(argv[2], NULL, 10); | ||
+ | } | ||
+ | |||
+ | MPI_Bcast(&m, 1, MPI_INT, 0, comm); | ||
+ | MPI_Bcast(&n, 1, MPI_INT, 0, comm); | ||
+ | |||
+ | //делим матрицу | ||
+ | int local_n = n / comm_sz; | ||
+ | int rem = n % comm_sz; | ||
+ | if(my_rank < rem) { | ||
+ | local_n++; | ||
+ | } | ||
+ | |||
+ | vector<vector<double> > G(local_n, vector<double> (m, -5)) , H(local_n, vector<double> (m, -5)); | ||
+ | |||
+ | //генирируем мартицу | ||
+ | srand(time(NULL)); | ||
+ | for(int i = 0; i < local_n; ++i) { | ||
+ | for(int j = 0; j < m; ++j) { | ||
+ | H[i][j] = rand(); | ||
+ | G[i][j] = rand(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | double s_time = 0; | ||
+ | if (rank == 0) { | ||
+ | s_time = MPI_Wtime(); | ||
+ | } | ||
+ | |||
+ | //ищем максимум в G благожелательно к лидеру на каждую его стратегию | ||
+ | vector<int> local_ind(local_n); | ||
+ | vector<double> local_max(local_n); | ||
+ | for(int i = 0; i < local_n; ++i){ | ||
+ | double max_in_line = G[i][0]; | ||
+ | int ind_max = 0; | ||
+ | for(int j = 0; j < m; ++j){ | ||
+ | if (G[i][j] > max_in_line) { | ||
+ | max_in_line = G[i][j]; | ||
+ | ind_max = j; | ||
+ | } else if (G[i][j] == max_in_line) { | ||
+ | if (H[i][j] > H[i][ind_max]) { | ||
+ | ind_max = j; | ||
+ | max_in_line = G[i][j]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | local_ind[i] = ind_max; | ||
+ | local_max[i] = H[i][ind_max]; | ||
+ | } | ||
+ | |||
+ | //ищем с учетом ответа второго наилучшую тратегию для лидера | ||
+ | double max_in_proc = local_max[0]; | ||
+ | for (int i = 0; i < local_n; ++i) { | ||
+ | if(max_in_proc < local_max[i]) { | ||
+ | max_in_proc = local_max[i]; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | double res_max; | ||
+ | |||
+ | MPI_Allreduce(&max_in_proc, &res_max, 1, MPI_DOUBLE, MPI_MAX, comm); | ||
+ | |||
+ | //ищем все равновесия по Штакельбергу | ||
+ | for (int i = 0; i < local_n; ++i) { | ||
+ | if(res_max == local_max[i]) { | ||
+ | //i, max_ind[i] равновесие по Штакельбергу | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //выводим время | ||
+ | double f_time = 0; | ||
+ | if (rank == 0) { | ||
+ | f_time = MPI_Wtime(); | ||
+ | cout << n << ' ' << f_time - s_time << endl; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | MPI_Finalize(); | ||
+ | |||
+ | } | ||
+ | </source> | ||
+ | |||
+ | == Литература == |
Текущая версия на 00:44, 6 декабря 2017
Основные авторы описания: Закирова С.В. Поиск равновесия в дуополии Штакельберга
Содержание
- 1 Свойства и структура алгоритмов
- 1.1 Общее описание алгоритма
- 1.2 Математическое описание алгоритма
- 1.3 Вычислительное ядро алгоритма
- 1.4 Макроструктура алгоритма
- 1.5 Схема реализации последовательного алгоритма
- 1.6 Последовательная сложность алгоритма
- 1.7 Информационный граф
- 1.8 Ресурс параллелизма алгоритма
- 1.9 Входные и выходные данные алгоритма
- 2 Программная реализация алгоритма
- 2.1 Особенности реализации последовательного алгоритма
- 2.2 Локальность данных и вычислений
- 2.3 Возможные способы и особенности параллельной реализации алгоритма
- 2.4 Масштабируемость алгоритма и его реализации
- 2.5 Динамические характеристики и эффективность реализации алгоритма
- 2.6 Выводы для классов архитектур
- 2.7 Существующие реализации алгоритма
- 3 Литература
1 Свойства и структура алгоритмов
1.1 Общее описание алгоритма
Модель дуополии Штакельберга является развитием модели дуополии Курно. Если в модели Курно считается, что участники рынка не прогнозируют отклика конкурента на собственные действия, то в модели Штакельберга один участник рынка не прогнозирует поведения конкурента, а второй учитывает поведение первого, зная, что конкурент не ответит на его действия. Другими словами, второй участник рынка знает, что первый участник рынка ведет себя в соответствии с моделью Курно. [1]
1.2 Математическое описание алгоритма
Есть множество стратегий первого игрока [math] X [/math] и множество стратегий [math] Y [/math] второго игрока. Первым ходит игрок, называемый лидером, его стратегия [math] x \in X [/math]. Второй игрок, называемый подчиненным, ходит стратегией [math] y \in Y [/math]. У каждого игрока есть своя функция выигрыша. Для лидера это функция [math] H(x,y) [/math]. Для подчиненного [math] G(x,y) [/math]. Оба хотят максимизировать свой выигрыш, при условии лояльности(лояльность, это когда при условии одинакового выигрыша для второго, он максимизирует выигрыш первого) второго игрока по отношении к первому. Набор стратегий [math] (x^*,y^*) [/math] называется равновесием Штакельберга[2], если [math] y^* = R(x^*) [/math] есть наилучший ответ подчиненного на стратегию лидера, которая находится как решение задачи [math] H(x^*, y^*) = max H(x,R(x)) [/math] (максимум берется по всем стратегиям [math]x[/math] из [math]X[/math])
1.3 Вычислительное ядро алгоритма
Вычислительным ядром данного алгоритма является поиск максимума в строке. Таким образом мы находим наилучший ответ второго игрока.
1.4 Макроструктура алгоритма
Как уже сказано в пункте выше, основнуая составляющая часть алгоритма это поиск максимума в строке.
Алгоритм можно представить как многократное повторение(по количеству строк) поиска максимума в строках первой матрицы. После этого поиск максимума по элементам второй матрицы, индексы которых уже были найдены в первой итерации.
1.5 Схема реализации последовательного алгоритма
#include <vector>
#include <cstdlib>
#include <ctime>
using namespace std;
int main(int argc, char *argv[])
{
//считиываем m и n
int n = strtol(argv[1], NULL, 10);
int m = strtol(argv[2], NULL, 10);
//создаем матрицы G и H
vector<vector<double>> H(n, vector<double>(m));
vector<vector<double>> G(n, vector<double>(m));
//инициализируем матрицы G и H
srand(time(NULL));
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
H[i][j] = rand();
G[i][j] = rand();
}
}
//ищем максимум в G благожелательно к лидеру на каждую его стратегию
vector<int> ind(n);
for(int i = 0; i < n; ++i){
double max_in_line = H[i][0];
int ind_max = 0;
for(int j = 0; j < m; ++j){
if (G[i][j] > max_in_line) {
max_in_line = G[i][j];
ind_max = j;
} else if (G[i][j] == max_in_line) {
if (H[i][j] > H[i][ind_max]) {
ind_max = j;
max_in_line = G[i][j];
}
}
}
ind[i] = ind_max;
}
//ищем с учетом ответа второго наилучшую тратегию для лидера
int str_h = 0;
for(int i = 0; i < n; ++i) {
if (H[i][ind[i]] > H[str_h][ind[str_h]]) {
str_h = i;
}
}
}
1.6 Последовательная сложность алгоритма
Для первой части - поиска наилучших стратегий для воторого потребуется [math] m * n [/math] операций сравенения.
Для второй части - поиска наилучших стратегий первого потребуется [math] n [/math] операций сравнения.
Таким образом результирующая сложность алгоритма - [math] О(n * m) [/math] операций.
1.7 Информационный граф
Опишем граф алгоритма в виде рисунка.
Круглые элеменеты графа - элементы матрицы G. Под max, имеется в виду функция поиска максимума в строке (причем с уметом благожелательности).
max2 - опять же функция поиска максимума для первого, исходя из ответов второго.
1.8 Ресурс параллелизма алгоритма
Каждую строку можно обрабатывать отдельным процессором. Таким образом каждый процессор бежит по строке и ищет там максимум (с условием благосклонности к лидеру). После этого процессоры синхронизируются и мы ищем максимум из уже найденых значений, таким образом мы получаем наибольший выигрыш для лидера, после чего находим все равновесия по Штакельбергу.
Параллельная сложность: [math]О(m)[/math], где [math]m[/math] число столбцов а нашей матрице.
1.9 Входные и выходные данные алгоритма
Входными данными являются пара матриц [math] H(x,y) [/math], [math] G(x,y) [/math] одинакового размера [math]n * m [/math] (стратегии обоих игроков). Выходные данные это множество пар чисел [math] (x^*,y^*) [/math], где каждая из пар является равновесием по Штакельбергу в нашей дуаполии.
2 Программная реализация алгоритма
2.1 Особенности реализации последовательного алгоритма
2.2 Локальность данных и вычислений
2.3 Возможные способы и особенности параллельной реализации алгоритма
2.4 Масштабируемость алгоритма и его реализации
Проведём исследование масштабируемости параллельной реализации алгоритма. Набор и границы значений изменяемых параметров запуска реализации алгоритма:
число процессоров [1,2 4, 8 : 32] со значениями квадрата целого числа;
размер матрицы(для тестов бралась квадратная матрица) [1000:10000] с шагом 1000.
На следующем рисунке приведен график производительности выбранной реализации алгоритма в зависимости от изменяемых параметров запуска.
сведения о программно-аппаратной среде
компьютер: суперкомпьютор Ломоносов
компилятор с использованными опциями оптимизации: mpicxx версии openmpi/1.8.4-gcc
библиотеки: #include <mpi.h>, #include <iostream>, #include <cstdlib>, #include <ctime>, #include <vector>
выводы о масштабируемости
Мы получили ожидаемые результаты(в целом), что производительность увеличивается с увеличеснием количества процессоров и уменьшается с увеличеннием количества строк. Это можно объяснить тем, что выбранный алгоритм очень хорошо распараллеливается, а «общение» процессоров почти не требуется (это важно так как эта процедура достаточно ресурсозатратна).
2.5 Динамические характеристики и эффективность реализации алгоритма
2.6 Выводы для классов архитектур
2.7 Существующие реализации алгоритма
Написанная мной, текущая реализация алгоритма:
#include <mpi.h>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
int main(int argc, char * argv[])
{
int comm_sz, my_rank;
MPI_Init(&argc, &argv);
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Comm_size(comm, &comm_sz);
MPI_Comm_rank(comm, &my_rank);
//считываем размеры матрицы
int n,m;
if (my_rank == 0) {
n = strtol(argv[1], NULL, 10);
m = strtol(argv[2], NULL, 10);
}
MPI_Bcast(&m, 1, MPI_INT, 0, comm);
MPI_Bcast(&n, 1, MPI_INT, 0, comm);
//делим матрицу
int local_n = n / comm_sz;
int rem = n % comm_sz;
if(my_rank < rem) {
local_n++;
}
vector<vector<double> > G(local_n, vector<double> (m, -5)) , H(local_n, vector<double> (m, -5));
//генирируем мартицу
srand(time(NULL));
for(int i = 0; i < local_n; ++i) {
for(int j = 0; j < m; ++j) {
H[i][j] = rand();
G[i][j] = rand();
}
}
double s_time = 0;
if (rank == 0) {
s_time = MPI_Wtime();
}
//ищем максимум в G благожелательно к лидеру на каждую его стратегию
vector<int> local_ind(local_n);
vector<double> local_max(local_n);
for(int i = 0; i < local_n; ++i){
double max_in_line = G[i][0];
int ind_max = 0;
for(int j = 0; j < m; ++j){
if (G[i][j] > max_in_line) {
max_in_line = G[i][j];
ind_max = j;
} else if (G[i][j] == max_in_line) {
if (H[i][j] > H[i][ind_max]) {
ind_max = j;
max_in_line = G[i][j];
}
}
}
local_ind[i] = ind_max;
local_max[i] = H[i][ind_max];
}
//ищем с учетом ответа второго наилучшую тратегию для лидера
double max_in_proc = local_max[0];
for (int i = 0; i < local_n; ++i) {
if(max_in_proc < local_max[i]) {
max_in_proc = local_max[i];
}
}
double res_max;
MPI_Allreduce(&max_in_proc, &res_max, 1, MPI_DOUBLE, MPI_MAX, comm);
//ищем все равновесия по Штакельбергу
for (int i = 0; i < local_n; ++i) {
if(res_max == local_max[i]) {
//i, max_ind[i] равновесие по Штакельбергу
}
}
//выводим время
double f_time = 0;
if (rank == 0) {
f_time = MPI_Wtime();
cout << n << ' ' << f_time - s_time << endl;
}
MPI_Finalize();
}