Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Математическая логика и теория алгоритмов. Анализ алгоритмов

.pdf
Скачиваний:
3
Добавлен:
12.11.2023
Размер:
8.87 Mб
Скачать

///MAX = 2*n + (2^(n - 1)) * (n * (11 * n + 7) + 22) + 9

///MIN = 2^(n - 1) * (9 * n * (n + 1) + 22) + 9

///EST = n + 2^n * (n * (5 * n + 4) + 11) + 9

vector<int> approximateSolution() { ///T = O(N^3) bool del[N]; ///1

for (int i = 0; i < n; i++) ///2+2*n

del[i] = false; ///n * 2 vector<int> ans;

for (int k = 0; k < n; k++) { ///2 + 2 * n int v1 = -1; ///n

int v2 = -1; ///n

for (int i = 0; i < n; i++) { ///n * (2 + 2 * n)

for (int j = i; j < n; j++) { ///n * (2 + 3 + ... + n + 1) = n * ((n^2 + 3*n + 2) / 2 - 1)

if (u[i][j] && !del[i] && !del[j] && v1 == -1) { /// 10 * n * n * (n - 1) /2

v1 = i; ///выполнение не более n раз - 2 * n * C1 v2 = j;

}

}

}

if (v1 != -1 && v2 != -1) { ///3 * n

del[v1] = del[v2] = true; ///C1 * n * (4 + 2 + 2 + 2) ans.push_back(v1 + 1);

if (v2 != v1) ans.push_back(v2 + 1);

}

}

sort(ans.begin(), ans.end()); ///n * log(n) return ans; ///1

///1+2+2*n+2*n+2+2*n+n+n+n*(2+2*n)+n*(n^2+3*n+2)/2-n+ 10*n*n*(n-1)/2+2*n*C1+3*n+C1*n*(10)+1

///MAX = 5*(n-1)*n*n + (n*n+3*n+2)*n/2 + (2*n+2)*n + 22*n + 6 + n * log(n)

161

///MIN = 5*(n-1)*n*n + (n*n+3*n+2)*n/2 + (2*n+2)*n + 10*n + 6 + n * log(n)

///EST = 5*(n-1)*n*n + (n*n+3*n+2)*n/2 + (2*n+2)*n + 16*n + 6 + n * log(n)

}

vector<int> greedySolution() { ///T = O(N^3) vector<int> ans; ///0

bool del[N][N]; ///0

for (int i = 0; i < n; i++) ///2+2*n

for (int j = 0; j < n; j++) ///n * (2 + 2*n) del[i][j] = false; ///3 * n * n

for (int iq = 0; iq < n; iq++) { ///2 + 2*n int v = -1; ///n

int deg = -1; ///n

for (int i = 0; i < n; i++) { ///n * (2+2*n) int cur_deg = 0; ///n*n

for (int j = 0; j < n; j++) { ///n*n*(2+2*n)

if (u[i][j] && !del[i][j]) { ///6*n^3

cur_deg++; ///лучший вариант - 0 раз, худший -

суммарно n * (n - 1) * n, среднее - n * n * (n - 1) / 2

}

}

if (cur_deg >= deg) { /// n*n

deg = cur_deg; /// данная ветка не более n раз v = i; /// 0 - min, n - max, n / 2 - est

}

}

if (deg == 0) continue; ///n

ans.push_back(v + 1); ///2 * n

for (int i = 0; i < n; i++) { ///n * (2 + 2*n) if (u[v][i]) { ///3*n*n

del[v][i] = del[i][v] = true; ///6*n*n - максимальное количество раз

}

}

162

}

sort(ans.begin(), ans.end());///n*log(n) return ans;///1

///2+2*n + n*(2+2*n) + 3*n*n + 2+2*n + n + n + n*(2+2*n) + n*n + n*n*(2+2*n) + 6*n*n*n + C1*n*n*(n-1)/4 + n*n + C2*n + n + 2*n + n*(2+2*n) + 3*n*n + C3*6*n*n + n*log(n) + 1

///MAX = 6*n^3 + 1/4*(n-1)*n*n + (2*n+2)*n*n + 14*n*n + 3*(2*n+2)*n + 10*n + 5 + n*log(n)

///MIN = 6*n^3 + (2*n + 2)*n^2 + 8*n^2 + 3*(2*n + 2)*n + 9*n + 5 + n*log(n)

///EST = 6*n^3 + 1/32*(n - 1)*n^2 + (2*n + 2)*n^2 + 11*n^2 + 3*(2*n + 2)*n + (19*n)/2 + 5 + n*log(n)

}

void genRand(int nn, int perc = 30) { srand(3431);

n = nn;

for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) {

if (i == j) { u[i][j] = false;

} else if (i < j) {

u[i][j] = ((rand() % 100) < perc); } else {

u[i][j] = u[j][i];

}

}

}

}

void genAntiGreed(int nn) { n = nn;

int x = 0;

if (n >= 18) x = 6; if (n >= 40) x = 12; if (n >= 84) x = 24;

163

for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)

u[i][j] = false; int ps = x;

for (int d = x; d >= 1; d--) { if (x % d) continue;

for (int j = 0; j < x; j++) { int v1 = j;

int v2 = ps + j % d; u[v1][v2] = u[v2][v1] = true;

}

ps += d;

}

}

void genAntiApprox(int nn) { n = nn;

int a = n / 2;

for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)

u[i][j] = false;

for (int i = 0; i < a; i++) { int v1 = i;

int v2 = a + i;

u[v1][v2] = u[v2][v1] = true;

}

}

void genForApprox(int nn) { n = nn;

for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)

u[i][j] = false; u[0][1] = u[1][0] = true; for (int i = 2; i < n; i++) {

u[i][i & 1] = u[i & 1][i] = true;

}

}

164

void genForGreedy(int nn) { n = nn;

for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)

u[i][j] = false;

for (int i = 0; i < n; i += 4) { for (int x = 0; x < 4; x++) for (int y = 0; y < 4; y++) if (x != y && x + y != 2) u[i + x][i + y] = true;

}

}

void genSimple(int nn, bool is_edge) { n = nn;

for (int i = 0; i < n; i++) for (int j = 0; j < n; j++)

u[i][j] = is_edge;

}

void go(int res, int ans, int &num_ok, double &fail, const string &name = "kk") {

if (name == "approx" && res > ans * 2) {

cout << "approx is too bad " << res << " " << ans << "\n"; exit(11);

}

if (res == ans) num_ok++; if (ans > 0) {

fail += (abs(res - ans) + 0.0) / (ans + 0.0);

}

}

int main() { cout.precision(3); //readData(); //genRand(27);

165

//genAntiGreed(84);

//genAntiApprox(20);

//genForApprox(20);

//genForGreedy(24);

/*cout << "bruteForce: \n" << bruteForceSolution() << "\n"; cout << "approximate: \n" << approximateSolution() << "\n"; cout << "greedy: \n" << greedySolution() << "\n";

return 0;

*/

genSimple(700, false); clock_t t0 = clock(); for (int i = 0; i < 10; i++)

greedySolution();

cout << "Greedy Solution avg time on best test (N=700): " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * 10.0) << "\n";

genSimple(700, true); t0 = clock();

for (int i = 0; i < 10; i++) greedySolution();

cout << "Greedy Solution avg time on worst test (N=700): " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * 10.0) << "\n";

genSimple(700, false); t0 = clock();

for (int i = 0; i < 10; i++) approximateSolution();

cout << "Approximate Solution avg time on best test (N=700): " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * 10.0) << "\n";

genSimple(700, true); t0 = clock();

for (int i = 0; i < 10; i++) approximateSolution();

double tme = (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * 10.0);

166

cout << "Approximate Solution avg time on worst test (N=700): " << fixed << tme << "\n";

cout << " T(1) = time / (10*N^3) = " << tme / (10 * 700 * 700 * 700.0 * 1e-9) << "mcs\n";

vector<int> V = {5, 10, 15, 18, 20, 21};

for (int v: V) {

cout << "N = " << v << "\n"; vector<int> ans;

clock_t t0 = clock();

for (int perc = 10; perc <= 100; perc += 10) { genRand(v, perc); ans.push_back(bruteForceSolution().size());

}

genForApprox(v); ans.push_back(bruteForceSolution().size()); genAntiApprox(v); ans.push_back(bruteForceSolution().size()); genForGreedy(v); ans.push_back(bruteForceSolution().size()); genAntiGreed(v); ans.push_back(bruteForceSolution().size()); int tests = ans.size();

cout << " Brute Force avg time = " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * tests * 1.0) << " s" << endl;

///----------------APPROXIMATE---------------------

t0 = clock(); int cur = 0; int num_ok = 0; double fail = 0;

for (int perc = 10; perc <= 100; perc += 10) { genRand(v, perc);

go(approximateSolution().size(), ans[cur++], num_ok, fail, "approx");

167

}

genForApprox(v);

go(approximateSolution().size(), ans[cur++], num_ok, fail, "approx");

genAntiApprox(v);

go(approximateSolution().size(), ans[cur++], num_ok, fail, "approx");

genForGreedy(v);

go(approximateSolution().size(), ans[cur++], num_ok, fail, "approx");

genAntiGreed(v);

go(approximateSolution().size(), ans[cur++], num_ok, fail, "approx");

cout << " Approximate Solution avg time = " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * tests * 1.0) << " s" << endl;

cout << " Percent of correct answers: " << fixed << (num_ok * 100.0) / (tests + 0.0) << "%" << endl;

cout << " Average deviation: " << fixed << (fail * 100.0) / (tests + 0.0) << "%" << endl;

///----------------

GREEDY--------------------------

t0 = clock();

 

cur = 0;

 

num_ok = 0;

 

fail = 0;

for (int perc = 10; perc <= 100; perc += 10) { genRand(v, perc);

go(greedySolution().size(), ans[cur++], num_ok, fail);

}

genForApprox(v);

go(greedySolution().size(), ans[cur++], num_ok, fail); genAntiApprox(v);

go(greedySolution().size(), ans[cur++], num_ok, fail); genForGreedy(v);

168

go(greedySolution().size(), ans[cur++], num_ok, fail); genAntiGreed(v);

go(greedySolution().size(), ans[cur++], num_ok, fail);

cout << " Approximate Solution avg time = " << fixed << (clock() - t0 + 0.0) / (CLOCKS_PER_SEC * tests * 1.0) << " s" << endl;

cout << " Percent of correct answers: " << fixed << (num_ok * 100.0) / (tests + 0.0) << "%" << endl;

cout << " Average deviation: " << fixed << (fail * 100.0) / (tests + 0.0) << "%" << endl;

}

return 0;

}

169

ПРИЛОЖЕНИЕ В

Профилирование программ

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

Профилировщики играют важнейшую роль при оптимизации программ наряду с теоретической оценкой сложности этих программ. В силу огромного объема исходного кода современных систем построить функции сложности для каждой используемой подпрограммы не представляется возможным. Но в большинстве случаев этого и не требуется: основную часть времени занимает выполнение относительно небольших фрагментов исходного кода, и именно эти фрагменты требуют оптимизации в первую очередь. Для локализации таких фрагментов, по крайней мере, на уровне подпрограмм и используется профилирование. Оно показывает, какие подпрограммы занимают основное время работы приложения. Дальнейшая оптимизация заключается в анализе сложности этих подпрограмм и поиске более эффективных алгоритмов.

Современные среды разработки содержат встроенные средства профилирования. Например, профилировщик есть в среде разработки Visual Studio.

К возможным методам профилирования в Visual Studio можно отнести sampling (легковесный метод для сбора статистических данных о работе приложения), CPU Usage (сбор детализированной информации о времени работы каждой вызванной

170