Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Виды и свойства алгоритмов - Курсовая.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
55.7 Кб
Скачать
  1. Решение задачи Майхилла (о стрелках)

    1. Постановка задачи

Имеется цепь стрелков и офицер. Каждый находящийся в цепи солдат может общаться только со своими соседями справа и слева. Офицер размещается на фланге цепи и может подавать команды только крайнему в цепи стрелку. Общее количество стрелков в цепи каждому из стрелков неизвестно. Общаться каждый из стрелков может только со своими соседями справа и слева. Требуется обеспечить одновременный залп всех стрелков цепи после подачи команды офицером.

    1. Решение задачи

Данная задача решается при помощи автоматной модели поведения стрелка.

На рисунке 1 представлена структурная модель поведения стрелков в задаче Майхилла. Дополнительно к стрелкам в задачу введены блок, моделирующий поведение офицера (он отдает команду стрелкам), и блок, выполняющий протоколирование внутренних состояний объектов, составляющих модель цепи стрелков.

Перечень блоков

  • Officer – модель офицера;

  • Rifleman 1, 2, … N – модели стрелков;

  • WriteStates – блок протокола.

Блоки Officer и WriteStates не обязательные. Первый введен для удобства управления цепью стрелков, второй – для визуализации решения.

Функционирование модели

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

Рисунок 1 – Модель поведения стрелков в задаче Майхилла

Решение задачи на языке высокого уровня С++

Применяя автоматную модель поведения стрелков, выполним решение данной задачи на языке высокого уровня С++.

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

Листинг программ сервера («Офицер») и клиента («Стрелок») представлен ниже.

Программа-сервер («Офицер»)

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

#include <vcl.h>

#include <iostream.h>

#pragma hdrstop

#include "Unit1.h"

#define BUFSIZE 512

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

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm1 *Form1;

HANDLE hThread;

HANDLE hProcPipe[5];

LPTSTR lpszPipename = "\\\\.\\pipe\\pipe1001";

HANDLE hPipe;

HANDLE hSemaphoreCount; //дескриптор семафора "Счет"

int SemaphoreMax=5; // максимальный размер счетчика семафора, и его начальное

//значение

int Count=0;

void ConnectShooters(LPVOID); //создание именованного канала для общения с

//клиентом

void ShooterThread(LPVOID); //поток для работы с клиентом

void Shoot(); //произвести выстрел

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

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

{

randomize();

for (int i=0; i<5; i++)

{

hProcPipe[i]=NULL;

}

}

//создание именованного канала для общения с клиентом

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

void ConnectShooters(LPVOID lpvParam)

{

int Number=0;

while (Number<5)

{

bool fConnected;

hPipe = CreateNamedPipe(lpszPipename,

PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE |

PIPE_READMODE_MESSAGE |

PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES,

BUFSIZE,

BUFSIZE,

0,

NULL);

fConnected = ConnectNamedPipe(hPipe, NULL);

if (fConnected)

{

hProcPipe[Number]=hPipe;

//создание потока для работы с клиентом

hThread = CreateThread (NULL,

0,

(LPTHREAD_START_ROUTINE) ShooterThread,

(LPVOID) Number,

0,

NULL);

Number++;

}

else

CloseHandle(hPipe);

}

}

//поток для работы с клиентом

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

void ShooterThread(LPVOID lpvParam)

{

char chBuf[BUFSIZE];

DWORD cbRead,cbWritten;

bool fSuccess;

int n=(int)lpvParam;

while (1)

{

//считывание ответов клиента из канала

fSuccess = ReadFile(hProcPipe[n],

chBuf,

BUFSIZE,

&cbRead,

NULL);

if (fSuccess)

{

int result=atoi(chBuf);

switch (result)

{

// в случае ответа клиента "1" идет прямой счет

case (1):

Count++;

Form1->StringGrid1->Cells[0][Count-1]= IntToStr(Count)+"-й";

break;

//в случае ответа клиента "2" идет обратный счет

case (2):

Form1->StringGrid1->Cells[1][Count-1]= IntToStr(Count)+"-й команду принял";

Count--;

//при окончании обратного счета производим выстрел

if (Count == 0) Shoot();

break;

default: break;

}

}

else break;

}

CloseHandle(hProcPipe[n]);

hProcPipe[n]=NULL;

}

//выстрел

void Shoot()

{

for (int i = 1; i <= 5; i++)

Form1->StringGrid1->Cells[2][i-1]= "Выстрел";

Form1->Button1->Enabled=true;

}

void __fastcall TForm1::Button1Click(TObject *Sender)

{

STARTUPINFO StartupInfo;

PROCESS_INFORMATION ProcessInfo;

Form1->Button1->Enabled=false;

hThread = CreateThread (NULL,

0,

(LPTHREAD_START_ROUTINE) ConnectShooters,

(LPVOID) 0,

0,

NULL);

if (hThread == NULL) {PrintInfo("Ошибка при создании потока"); return;}

//для каждого стрелка создаем процесс

for (int i=0; i<5; i++)

{

ZeroMemory(&StartupInfo,sizeof (StartupInfo));

StartupInfo.cb=sizeof(StartupInfo);

StartupInfo.dwFlags = STARTF_USESHOWWINDOW;

StartupInfo.wShowWindow = SW_HIDE;

//параметр командной строки, определяющий очередность счета

// "стрелков-клиентов"

int ShooterSleep=(i+1)*1000;

AnsiString CommandLine="";

CommandLine="Shooter "+IntToStr(i)+" "+IntToStr(ShooterSleep);

if (!

CreateProcess(NULL,

CommandLine.c_str(), //Указатель на строку,

//определяющую командную строку для выполнения

NULL,

NULL,

false,

0,

NULL,

GetCurrentDir().c_str(), // имя текущего каталога

&StartupInfo, // информация предустановки

&ProcessInfo)) // информация о процессе

{PrintInfo("Не могу создать процесс"); return;}

hProcPipe[i]=ProcessInfo.hProcess;

}

//создаем семафор "Счет"

hSemaphoreCount = CreateSemaphore(NULL, // Адрес структуры TSecurityAttributes

SemaphoreMax, //начальное значение счетчика

SemaphoreMax, //максимальное значение

//счетчика

"SemaphoreCount"); //имя объекта

}

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

Программа-клиент («Стрелок»)

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

#include <vcl.h>

#include <iostream.h>

#define BUFSIZE 512

#define N 5

#pragma hdrstop

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

#pragma argsused

LPTSTR lpszPipename="\\\\.\\pipe\\pipe1001";

HANDLE hPipe;

HANDLE hSemaphoreCount; //дескриптор семафора "Счет"

//отправка сообщения на сервер

void MsgToServer(int Message)

{

char chBuf[BUFSIZE];

bool fSuccess;

DWORD cbWritten;

AnsiString S=IntToStr(Message);

wsprintf(chBuf,(const char*)S.c_str(),"/0");

fSuccess =WriteFile(hPipe,

chBuf,

BUFSIZE,

&cbWritten,

NULL);

}

//основная функция

int main(int argc, char* argv[])

{

randomize();

char chBuf[BUFSIZE];

DWORD cbRead, cbWritten, dwMode;

//аргумент командной строки, отвечающий за очередность счета стрелков

int ShooterSleep=atoi(argv[2]);

//получение дескриптора канала

while (1)

{

hPipe = CreateFile (lpszPipename,

GENERIC_READ |

GENERIC_WRITE,

FILE_SHARE_READ,

NULL,

OPEN_EXISTING,

0,

NULL);

if (hPipe != INVALID_HANDLE_VALUE) break;

if (GetLastError() != ERROR_PIPE_BUSY) return 1;

if (!WaitNamedPipe(lpszPipename, 20000)) return 1;

}

Sleep(ShooterSleep);

while (1)

{

//получение дескриптора семафора "Счет"

hSemaphoreCount = OpenSemaphore(SEMAPHORE_ALL_ACCESS, 0, "SemaphoreCount");

//"засыпание" потока для выполнения очередности счета

Sleep(ShooterSleep);

//если семафор в сигнальном состоянии, то идет прямой счет

//иначе выход из цикла, далее - обратный счет

if (WaitForSingleObject(hSemaphoreCount,0) == WAIT_OBJECT_0) MsgToServer(1);

else break;

}

//обратный счет

MsgToServer(2);

//"засыпание" для выполнения очередности обратного счета

Sleep((5000-ShooterSleep)*2);

//закрытие дескрипторов

CloseHandle(hPipe);

CloseHandle(hSemaphoreCount);

return 0;

}

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