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).