cin >> n;
sum = 0;
for (i=0; i<n; i++)
for (j=0; j<n; j++) sum++;
* Колко бързо ще
работи горната програма, т.е. какви са критериите по които
се определя бързината й? * Eкспериментално да проверим за колко време ще се изпълни програмата. * За да изследваме по-общо нейното поведение е ще я изпълним с различни стойности на n. * Резултатите са обобщени в следната таблица (могат да бъдат такива): |
|
* Сравняване на две
функции: g1(n)= 2n2 и g2(n)= 200n, които показват времето за изпълнение на два дадени алгоритъма А1 и A2, в зависимост от n. * Асимптотично алгоритъмът A2 е по-бърз и неговата сложност е линейна, докато тази на A1 е квадратична. |
|
* Нека е дадена задача, в която размерът на
входните данни е определен от дадено цяло число n.
* Почти всички задачи, които ще разглеждаме, притежават това
свойство.
* Ще поясним последното като разгледаме няколко примера:
Пример 1.
Да се сортира масив с n елемента.
Размерът на входните данни се определя от
броя n на елементите на масива .
Пример 2.
Да се намери най-големият общ делител на a
и b.
В този пример размерът на входните данни се
определя от броя на двоичните цифри (битовете) на по-голямото от
числата a и b.
Пример 3.
Да се намери покриващо дърво на граф.
В този случай характеризираме размера на
входа с две числа: брой на върховете и брой на ребрата.
* Когато се интересуваме от сложността на
алгоритъм най-често се интересуваме как ще работи при достатъчно
голям размер n на
входните данни.
* При формалното оценяване на сложността на алгоритмите
изследваме поведението им при "достатъчно голямо" n
(клонящо към безкрайност).
1. O(f) определя множеството от всички функции g, които нарастват не по-бързо от f, т.е. съществува константа c > 0 такава, че g (n) <= cf(n), за всички достатъчно големи стойности на n.
2. Theta (f) определя множеството от всички функции g, които нарастват толкова бързо, колкото и f (с точност до константен множител), т.е. съществуват константи c1 > 0 и c2 > 0 такава, че c1f(n) <= g (n) <= c2f(n), за всички достатъчно големи стойности на n.
3. Omega (f) определя множеството от всички функции g, които нарастват не по-бавно от f, т.е. съществува константа c > 0 такава, че g(n) >= cf(n), за всички достатъчно големи стойности на n.
O(f): Свойства и примери
* Нотацията О(f) е най-често
използваната при оценка на сложност на алгоритми и програми.
* По-важни свойства на О(f)
(с ~ означаваме принадлежност):
Нарастване на най-често използваните функции:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
** Определяне на сложност на алгоритъм.
* Намиране на функцията, определяща зависимостта между
големината на входните данни и времето за изпълнение.
* Винаги разглеждаме най-лошия случай - при най-неблагоприянти
входни данни.
- елементарна операция - не зависи от
размера на обработваните данни - O(1) ;
- последователност от оператори - определя
се от асимтотично най-бавния - f + g ~ max(O( f ), O(g));
- композиция на оператори - произведение от
сложностите - f (g) ~ O( f*g);
- условни оператори - определя се от
асимтотично най-бавния между условието и различните случаи;
- цикли, два вложени цикъла, p
вложени цикли - O(n), O(n2), O(np) .
// 1
for (i = 0; i < n; i++)
for (j = 0; j < n; j++,
sum++);
// 2
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) if
(a[i] == b[j]) return;
// 3
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) if
(a[i] != b[j]) return;
// 4
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) if
(a[i] == a[j]) return;
// 5
for (i = 0; i < n; i++)
for (j = 0; j < i; j++)
sum++;
// 6
for (i = 0; i < n; i++)
for (j = 0; j < n*n; j++)
sum++;
// 7
for (i = 0; i < n; i++)
for (j = 0; j < i*i; j++)
sum++;
// 8
for (i = 0; i < n; i++)
for (j = 0; j < i*i; j++)
for (k = 0; k < j*j;
k++) sum++;
Да разгледаме цикъла:
for (sum = 0, i = 0; i < n; i *=
2) sum++;
Променливата i приема стойности 1, 2, 4, ..., 2k,
... докато надмине n. Цикълът се изпълнява
[log n] пъти. Сложността е O(log n).
Изчисляване на сложност при
рекурсия.
* Двоично търсене в сортиран масив - рекурсивен алгоритъм.
int binary_search(vector<int> v, int from, int to, int a) { if (from > to) return -1; int mid = (from + to) / 2; if (v[mid] == a) return mid; else if (v[mid] < a) return binary_search(v, mid + 1, to, a); else return binary_search(v, from, mid - 1, a); }
* Броим обръщенията към елементите на масива.
* В рекурсивната функция се разглежда средния елемент и се прави
едно рекурсивно извикване с два пъти по-малък масив.
* Следователно, ако T(n) е функцията, която задава
броя на обръщенията, то T(n) = T(n/2)
+ 1.
* От равенствата
T(n) = T(n/2) + 1 = T(n/4)
+ 2 = T(n/8) + 3 = ... = T(n/2k)
+ k
получаваме за n = 2k, че T(n)
= T(1) + log n, т.е. сложността на алгоритъма е O(log
n).
*** Бройни системи [1.1.6]
Пример за задача за домашно
Задача 0. Да се напише
програма за превръщане на естествено число от десетична в двоична
бройна система и обратно.
Вход:
На стандартния вход за един тестов пример на един ред се
задава основата на бройната система (2 или 10) и редица от числа в
тази бройна система. Входът съдържа много тестови примери.
Ограничения:
Всички числа са по-малки от 105.
Изход:
За всеки тестов пример на отделен ред се отпечатва основата на
бройната система (2 или 10) и редицата от задените числа в тази
бройна система.
Пример:
10 8 1 2
2 1000
2 1 10 111 1111
Решение на примера:
2 1000 1 10
10 8
10 1 2 15 63
Пример за проверка (0.inp) |
Изход: (0.sol) |
10 1 10 100 100 1000 9999 2 11111111 10101010 2 1 10 100 1000 10000 1000000 10000000 100000000 1000000000 10000000000 100000000000 2 10011100001111 10 10 10 10 10 10 10 10 10 10 10 |
2 1 1010 1100100 1100100
1111101000 10011100001111 10 255 170 10 1 2 4 8 16 64 128 256 512 1024 2048 10 9999 2 1010 1010 1010 1010 1010 1010 1010 1010 1010 1010 |
Програма:
// f12345_0.cpp
#include <iostream>
#include <string>
#include<sstream>
using namespace std;
string ten2two(int n)
{
string m;
while (n >= 1)
{
m =
static_cast<char>((n%2) + '0') + m;
n /=2;
}
return m;
}
int two2ten(string s)
{
int m = 0;
for (int i = 0; i < s.length(); i++) m =
2*m + (s[i] - '0');
return m;
}
int main()
{
string s;
while(getline(cin, s))
{
int p;
istringstream
is(s);
is >> p;
int n;
string sn;
if (p == 10)
{
cout << "2 ";
while(is >> n)
cout << ten2two(n) << " ";
}
else
{
cout << "10 ";
while(is >> sn)
cout << two2ten(sn) << " ";
}
cout << endl;
}
return 0;
}
Проверка:
mini:CSCB324 nkirov$ ./ad.sh 0 12345
Скрипт за проверка:
c++ f$2_$1.cpp
time ./a.out < $1.inp
./a.out < $1.inp > $1s.sol
rm a.out
diff $1s.sol $1.sol