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

2.3. Оценка сложности алгоритма

Как было описано выше, сложность алгоритма: O(n log2n). Действительно, рассмотрим алгоритм (в худшем случае, когда все точки множества являются точками выпуклой оболочки):

  • нахождение выпуклой оболочки: O(n log2n);

  • выбор крайних по x точек: O(n);

  • основной ход алгоритма: O(n).

3. Примеры работы алгоритма

Интересно выделить следующие ситуации:

  • все точки множества являются точками выпуклой оболочки (см. рис. 1);

  • большинство точек множества не являются точками выпуклой оболочки, только три точки (треугольник) являются точками выпуклой оболочки (см. рис. 2);

  • произвольный случай множества (см. рис. 3).

Рис. 1

Рис. 2

Рис. 3

Описание демонстрационной программы приведено в документе «Руководство пользователя».

4. Описание программы

Подробное описание программы с точки зрения пользователя приведено в документе «Руководство пользователя». Ниже описаны основные функции программы, структура (компоненты), исходные тексты.

4.1. Функции программы

Основными функциями программы являются следующие:

  • построение выпуклой оболочки (используется алгоритм Джарвиса, текст которого на языке программирования С++ приведен в пункте 4.3. Исходные тексты);

  • нахождение диаметра множества:

  • использование пошагового режима;

  • использование режима предугадывания очередного шага;

  • использование автоматического режима.

При использовании автоматического режима нахождения диаметра множества весь алгоритм выполняется сразу, пользователю при этом выдается информация о найденном диаметре, изображается сам диаметр. Кроме того, в протоколе (вызывается при нажатии на кнопку панели инструментов или при выборе пункта меню Вид->Протокол) отображаются все шаги алгоритма.

4.2. Структура программы

Реализованная демонстрационная программа является однодокументным MFC-приложением, работающим в среде Microsoft Windows. Программа состоит из основного графического окна (и соответствующего ему документа), а также ряда вспомогательных диалоговых окон:

  • справочная информация;

  • информация об алгоритме;

  • настройки генерации множества точек;

  • протокол работы алгоритма.

Все точки множества хранятся в полиморфном контейнере. Работа с множеством осуществляется с использованием итератора для этого контейнера. В программе используются обработчики исключительных ситуаций.

Все диалоговые окна вызываются из главного при помощи выбора соответствующего пункта меню или при помощи нажатия на соответствующую кнопку на панели инструментов.

4.3. Исходные тексты

В данном разделе приведены исходные тексты на языке С++ ключевых участков программы.

1) Описание полиморфного контейнера

template <class T, class A = DiameterAlloc<T>, class C = std::list<T,A> >

class TDiameterCont : public C

{

public:

typedef DiameterIterator<T> iterator;

TDiameterCont(): C();

bool empty();

long size();

iterator begin()

iterator end();

// Возвращает первую вершину, имеющую заданный ID

T GetByID(const int ID) throw (DiameterError::EInvalidNodeID);

// Возвращает первую вершину, лежащую в е-окрестности заданной точки

T GetByXY(const int x, const int y, const double e) throw (DiameterError::EInvalidNodeXY);

void Insert(const T& value ) throw (DiameterError::EInvalidInsert);

void DeleteByID(const int ID) throw (DiameterError::EInvalidDelete);

void ClearDiameterCont() throw (DiameterError::EInvalidDelete);

float GetDistance (const T p1, const T p2);

void ScaleAll(const float xScale, const float yScale);

~TDiameterCont();

void UnMakeCH();

void MakeCH() throw (DiameterError::EInvalidCH); //построение выпуклой оболочки

};

2) Описание алгоритма нахождения выпуклой оболочки по методу Джарвиса

void MakeCH() throw (DiameterError::EInvalidCH) //построение выпуклой оболочки

{

try{

UnMakeCH();

//Джарвис

if (C::size()>2)

{

T MinPoint=0;

T MaxPoint=0;

iterator p = begin();

MinPoint=(*p);MaxPoint=(*p);

++p;

while (p != end())

{

if ((*p)->GetYCoord() < MinPoint->GetYCoord())

{

MinPoint=(*p);

}else if ((*p)->GetYCoord() == MinPoint->GetYCoord())

{

if ((*p)->GetXCoord() < MinPoint->GetXCoord())

{

MinPoint=(*p);

}

}

if ((*p)->GetYCoord() > MaxPoint->GetYCoord())

{

MaxPoint=(*p);

}else if ((*p)->GetYCoord() == MaxPoint->GetYCoord())

{

if ((*p)->GetXCoord() > MaxPoint->GetXCoord())

{

MaxPoint=(*p);

}

}

++p;

}//end of while

// from minimun to maximum

T iTempPoint1=0;

T iTempPoint2=0;

iTempPoint1=MinPoint;

p=begin();

iterator t=begin();

iterator t1=begin();++t1;

float n1,n2;

while(iTempPoint1!=MaxPoint) {

// init point 2

if(iTempPoint1!=(*t))

iTempPoint2=(*t);

else

iTempPoint2=(*t1);

// look for second point

for(p=begin();p!=end();++p) {

if((*p)!=iTempPoint1) {

n1 = (iTempPoint2->GetXCoord()-iTempPoint1->GetXCoord())*((*p)->GetYCoord()-iTempPoint1->GetYCoord());

n2 = ((*p)->GetXCoord()-iTempPoint1->GetXCoord())*(iTempPoint2->GetYCoord()-iTempPoint1->GetYCoord());

if(n1>n2)

iTempPoint2=(*p);

}

}

// set line from 1 to 2

iTempPoint1->SetRight(iTempPoint2);

iTempPoint2->SetLeft(iTempPoint1);

// set p1 to p2

iTempPoint1=iTempPoint2;

}//end of while

// from maximum to minimun

while(iTempPoint1!=MinPoint) {

// init point 2

if(iTempPoint1!=(*t))

iTempPoint2=(*t);

else

iTempPoint2=(*t1);

// look for second point

for(p=begin();p!=end();++p) {

if((*p)!=iTempPoint1) {

n1 = (iTempPoint2->GetXCoord()-iTempPoint1->GetXCoord())*((*p)->GetYCoord()-iTempPoint1->GetYCoord());

n2 = ((*p)->GetXCoord()-iTempPoint1->GetXCoord())*(iTempPoint2->GetYCoord()-iTempPoint1->GetYCoord());

if(n1>n2)

iTempPoint2=(*p);

}

}

// set line from 1 to 2

iTempPoint1->SetRight(iTempPoint2);

iTempPoint2->SetLeft(iTempPoint1);

// set p1 to p2

iTempPoint1=iTempPoint2;

}//end of while

}

}catch(...)

{

throw DiameterError::EInvalidCH("Ошибка при поиске выпуклой оболочки!");

}

}

3) Функция очередного шага работы алгоритма

int CDiameterView::NextStep(int AutType)

{

if (!CH_BUILD) return(-1);//если не построена ВО

CDiameterDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

switch (Step){

case 0:

{//ищем макс и мин по х

iter=0;

NumPare=0;

lDialog.mEdit="Начало алгоритма\r\n";

DiameterIterator<TNode*> p = (pDoc->Diameter.begin());

while (p != (pDoc->Diameter.end())) //ищем вершину из ВО (n)

{

if ((*p)->GetRight()!=0) break;

++p;

}

if (p==(pDoc->Diameter.end())) return (-1);

float MinX=1000000,MaxX=-1;

float MinY=-1,MaxY=1000000;

float tmp=-100;

TNode *tmpFir=(*p);

TNode *tmpCur=(*p);

while ((tmpCur!=tmpFir)||(tmp==-100)) //ищем противолежащие по X точки (n)

{

tmp=tmpCur->GetXCoord();

if (tmp<MinX)

{

MinX=tmp;MinY=tmpCur->GetYCoord();NodeP=tmpCur;

} else if ((tmp==MinX)&&(tmpCur->GetYCoord()>MinY))

{

MinX=tmp;MinY=tmpCur->GetYCoord();NodeP=tmpCur;

}

if (tmp>MaxX)

{

MaxX=tmp;MaxY=tmpCur->GetYCoord();NodeQ=tmpCur;

} else if ((tmp==MaxX)&&(tmpCur->GetYCoord()<MaxY))

{

MaxX=tmp;MaxY=tmpCur->GetYCoord();NodeQ=tmpCur;

}

tmpCur=tmpCur->GetRight();

}

NodeP0=NodeP;

NodeQ0=NodeQ;

DiamNodeP=NodeP;

DiamNodeQ=NodeQ;

MaxDiameter=pDoc->Diameter.GetDistance(NodeP,NodeQ);

Step=1;

break;

}

case 1:

{//выполняем очередной шаг алгоритма (while NodeQ!=NodeP0)

if ((NodeP==NodeQ0)&&(NodeQ==NodeP0))

{

Step=4;break;

}

if ((pDoc->Diameter.GetDistance(NodeP,NodeQ))>MaxDiameter)

{

DiamNodeP=NodeP;

DiamNodeQ=NodeQ;

MaxDiameter=pDoc->Diameter.GetDistance(NodeP,NodeQ);

}

NodeP1=NodeP->GetRight();

NodeQ1=NodeQ->GetRight();

float a1=-Angle(NodeP,NodeP1);

float a2=-Angle(NodeQ1,NodeQ);

if (a1<a2) {NodeP=NodeP1;}

else if (a1>a2) {NodeQ=NodeQ1;}

else

{

if ((pDoc->Diameter.GetDistance(NodeP1,NodeQ))>MaxDiameter)

{

DiamNodeP=NodeP1;

DiamNodeQ=NodeQ;

MaxDiameter=pDoc->Diameter.GetDistance(NodeP1,NodeQ);

}

if ((pDoc->Diameter.GetDistance(NodeP,NodeQ1))>MaxDiameter)

{

DiamNodeP=NodeP;

DiamNodeQ=NodeQ1;

MaxDiameter=pDoc->Diameter.GetDistance(NodeP1,NodeQ1);

}

NodeP=NodeP1;

NodeQ=NodeQ1;

}

if ((NodeP==NodeQ0)&&(NodeQ==NodeP0))

{

Step=4;break;

}

break;

}

}

iter++;

return 0;

}

4) Функция, вызывающаяся при нажатии на кнопку пошагового режима

void CDiameterView::OnAlgoritmNext()

{

// TODO: Add your command handler code here

CDiameterDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

int tmp=CH_BUILD;

ResetFlags();

CH_BUILD=tmp;

FIRST_ENABLED=true;

int CurStep=Step;

int res=NextStep(0);

if ((CurStep==2)&&(Step==1)) {CurStep=Step;res=NextStep(0);}

if ((CurStep==1)&&(Step==3)) {res=NextStep(0);}

if (res==-1)

{

AfxMessageBox("Error!");

return;

}

if (Step!=4) WriteToLog(pDoc);

if ((UserNodeP!=0)&&(UserNodeQ!=0))

{

if (Step==4) AfxMessageBox("Это был последний шаг!",0);

else

{

if ( ((UserNodeP==NodeP)&&(UserNodeQ==NodeQ)) ||

((UserNodeP==NodeQ)&&(UserNodeQ==NodeP)) )

{//угадал

AfxMessageBox("Вы правильно выбрали вершины!",0);

}else

{//не угадал

AfxMessageBox("Вы НЕ правильно выбрали вершины!",16);

}

}

}

UserNodeP=UserNodeQ=0;USER=false;

Invalidate();

if (Step==4)

{

NEXT_ENABLED=false;

ALL_ENABLED=false;

USER_ENABLED=false;

WriteToLog(pDoc);

NodeP=NodeQ=0;

char ss[20];

gcvt(MaxDiameter,8,ss);

CString Out="Диаметр: ";Out+=ss;

AfxMessageBox(Out);

}

}

5) Функция, вызывающаяся при нажатии на кнопку автоматического режима

void CDiameterView::OnAlgoritmAll()

{

// TODO: Add your command handler code here

CDiameterDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

int tmp=CH_BUILD;

ResetFlags();

CH_BUILD=tmp;

UserNodeP=UserNodeQ=0;USER=false;

FIRST_ENABLED=true;

while ((Step!=4)&&(iter<10000))

{

int CurStep=Step;

int res=NextStep(0);

if ((CurStep==2)&&(Step==1)) {CurStep=Step;res=NextStep(0);}

if ((CurStep==1)&&(Step==3)) {res=NextStep(0);}

if (res==-1)

{

AfxMessageBox("Error!");

return;

}

if (Step!=4) WriteToLog(pDoc);

if (Step==4)

{

NEXT_ENABLED=false;

ALL_ENABLED=false;

USER_ENABLED=false;

WriteToLog(pDoc);

NodeP=NodeQ=0;

char ss[20];

gcvt(MaxDiameter,8,ss);

CString Out="Диаметр: ";Out+=ss;

AfxMessageBox(Out);

break;

}

}

Invalidate();

if (iter==10000) AfxMessageBox("Too many!");

}

8

Соседние файлы в папке doc