8.  Търсене

Последователно търсене [4, стр. 231]

** Човешката дейност търсене

** Опростен модел на търсене

** Фундаментални операции над елемнтите на множество (правилна оценка на ефективността на алгоритмите за търсене)
-- инициализиране
-- търсене
-- вмъкване
-- изтриване
-- обединяване на множества
--  сортиране.

** Ключ, повтарящи се ключове
-- има ли елемент с даден ключ? [bool exists(unsigned key)]
-- индекс на елемент (обект) с даден ключ? [unsigned find_one(unsigned key)]
-- брой елементи с даден ключ? [unsigned count(unsigned key)]
-- индекси на всички елементи (обекти) с даден ключ? [vector<unsigned> find_all(unsigned key)]

** Последователно (линейно) търсене
Проверяваме последователно елементите на множеството (което е линейно наредено), докато или намерим търсения елемент или стигнем до края на редицата.
Ефективност на алгоритъма за линейно търсене:
Броят на обръщенията към елементите на масива зависи от търсеното число, но в най-лошия случай, когато числото не се среща в масива, е равен на дължината на масива. Следователно сложността на алгоритъма е O(n).
// lsearch.cpp

int linear_search(vector<int> v, int a)
{
   int i;
   for (i = 0; i < v.size(); i++)
       if (v[i] == a) return i;
   return -1;
}

** Последователно търсене в сортиран списък с поддържане на сортирания списък при вмъкване на нов елемент.

** Последователно търсене с преподреждане

Двоично търсене (разделай и владей) [4.3, стр. 239]

// bsearch.cpp

int binary_search(vector<int> v, int from, int to, int a)
{
   if (from > to)  return -1;
   int mid = (from + to) / 2;
   int diff = v[mid] - a;
   if (diff == 0) return mid;          /* v[mid] == a */
   else if (diff < 0)                  /* v[mid] < a */
      return binary_search(v, mid + 1, to, a);
   else
      return binary_search(v, from, mid - 1, a);
}

Търсене и сортиране на реални данни
// esearch.cpp

int binary_search(vector<Employee> v, int from, int to, string n)
{
   if (from > to) return -1;
   int mid = (from + to) / 2;
   if (v[mid].get_name() == n)  return mid;
   else if (v[mid].get_name() < n)
      return binary_search(v, mid + 1, to, n);
   else
      return binary_search(v, from, mid - 1, n);
}

Tърсене с връщане

Класификация на задачите [6.1]
Сложност по време [6.1.1]
NP- задачи: Класът на полиномиално проверимите задачи
P- задачи: Съществува решение с полиномиална сложност
P = NP ???
Chasing complexity
Ryan Williams has taken a key step toward solving the biggest problem in theoretical computer science.

Експоненциални задачи: Съществува алгоритъм с експоненциална сложност
Сложност по памет [6.1.2]
Нерешими задачи [6.1.3]
Задачи, за които съществува доказателство, че не могат да бъдат решени, независимо от това с колко време и памет разполагаме.
Примери [6.1.4]
Полиномиално решими задачи: търсене, сортиране, ...
NP- задачи: Съществуване на делител на цяло положително число, по-малък от зададено число.
Класификация по памет: игра на шах
Нерешими задачи: Дали дадена програма е в безкраен цикъл.

int main()
{
    unsigned a, b , c, n, u;
    for (u = 3;;)
    {
        for (a = 1; a < u; a++)
            for (b = 1; b < u; b++)
                for (c=1; c < u; c++)
                    for (n=3; n < u; n++)
                        if (pow(a, n) + pow(b, n) == pow(c, n)) exit(0);
        u++;
    }
    return 0;
}
Неопределени задачи: Задача на Сколем - За дадена матрица дали съществува число n такова, че матрицата, повдигната на степен n съдържа нула в горния десен ъгъл.

NP-пълни задачи [6.2]
(Wiki)

Търсене с връщане [6.3]

Разходка на коня [6.3.4]
// knight.c
#include <stdio.h>

#include <stdlib.h>

#define MAXN 10

#define MAXD 10

const unsigned n = 6;

const unsigned startX = 1;
const unsigned startY = 1;

const unsigned maxDiff = 8;
const int diffX[MAXD] = { 1, 1, -1, -1, 2, -2, 2, -2 };
const int diffY[MAXD] = { 2, -2, 2, -2, 1, 1, -1, -1 };

unsigned board[MAXN][MAXN];
unsigned newX, newY;

void printBoard(void)
{ unsigned i, j;
  for (i = n; i > 0; i--) {
    for (j = 0; j < n; j++) printf("%3u", board[i-1][j]);
    printf("\n");
  }
  exit(0);
}

void nextMove(unsigned X, unsigned Y, unsigned i)
{ unsigned k;
  board[X][Y] = i;
  if (i == n * n) { printBoard(); return; }
  for (k = 0; k < maxDiff; k++) {
    newX = X + diffX[k]; newY = Y + diffY[k];
    if ((newX >= 0 && newX < n && newY >= 0 && newY < n) && (0 == board[newX][newY]))
      nextMove(newX, newY, i + 1);
  }
  board[X][Y] = 0;
}

int main(void) {
  unsigned i, j;
  for (i = 0; i < n; i++)
    for (j = 0; j < n; j++) board[i][j] = 0;
  nextMove(startX-1, startY-1, 1);
  printf("No solution. \n");
  return 0;
}

Задача за осемте царици [6.3.5]
// queens.c
#include<stdio.h>
#include<stdlib.h>

#define MAXN 100

const unsigned n = 13;

unsigned col[MAXN] , RD[2*MAXN - 1],
         LD[2*MAXN], queens [MAXN];

void printBoard()
{ unsigned i , j ;
  for (i = 0; i < n; i++) {
    printf("\n");
    for (j = 0; j < n; j++)
      if(queens[i] == j) printf("x ");
      else printf(". ");
  }
  printf("\n");
  exit(0);
}

void generate(unsigned i)
{ if(i == n) printBoard();
  unsigned k;   
  for (k = 0; k <= n; k++) {
    if (col[k] && RD[i + k] && LD[n + i - k]) {
      col[k] = 0; RD[i + k] = 0; LD[n + i - k] = 0; queens[i] = k;
      generate(i + 1);
      col[k] = 1; RD[i + k] = 1; LD[n + i - k] = 1;
    }
  }
}

int main(void) {
  unsigned i;
  for (i = 0; i < n; i++) col[i] = 1;
  for (i = 0; i < (2*n - 1); i++) RD[i] = 1;
  for (i = 0; i < 2*n; i++) LD[i] = 1;
  generate(0);
  printf("No solution!\n");
  return 0;
}

Метод на разклоненията и границите [6.4]
http://optlab-server.sce.carleton.ca/POAnimations2007/BranchAndBound.html

Задача за раницата