Решение задачи о ранце на многопроцессорных системах с общей памятью
Особенности технологии параллельного программирования, описание компилятора OpenMP (Open Multi-Processing) и MPI (Message Passing Interface). Постановка задачи о ранце и пример ее решения на С++. Решение задачи о ранце на OpenMP со многими потоками.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | магистерская работа |
Язык | русский |
Дата добавления | 08.03.2012 |
Размер файла | 1,8 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Прием/передача сообщений с блокировкой
int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm)
* buf - адрес начала буфера посылки сообщения
* count - число передаваемых элементов в сообщении
* datatype - тип передаваемых элементов
* dest - номер процесса-получателя
* msgtag - идентификатор сообщения
* comm - идентификатор группы
Блокирующая посылка сообщения с идентификатором msgtag, состоящего из count элементов типа datatype, процессу с номером dest. Все элементы сообщения расположены подряд в буфере buf. Значение
count может быть нулем. Тип передаваемых элементов datatype должен указываться с помощью предопределенных констант типа. Разрешается передавать сообщение самому себе.Блокировка гарантирует корректность повторного использования всех параметров после возврата из подпрограммы. Выбор способа осуществления этой гарантии: копирование в промежуточный буфер или непосредственная передача процессу dest, остается за MPI. Следует специально отметить, что возврат из подпрограммы MPI_Send не означает ни того, что сообщение уже передано процессу dest, ни того, что сообщение покинуло процессорный элемент, на котором выполняется процесс, выполнивший MPI_Send.
int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Status *status)
* OUT buf - адрес начала буфера приема сообщения
* count - максимальное число элементов в принимаемом сообщении
* datatype - тип элементов принимаемого сообщения
* source - номер процесса-отправителя
* msgtag - идентификатор принимаемого сообщения
* comm - идентификатор группы
* OUT status - параметры принятого сообщения
Прием сообщения с идентификатором msgtag от процесса source с блокировкой. Число элементов в принимаемом сообщении не должно превосходить значения count. Если число принятых элементов меньше значения count, то гарантируется, что в буфере buf изменятся только элементы, соответствующие элементам принятого сообщения. Если нужно узнать точное число элементов в сообщении, то можно воспользоваться подпрограммой MPI_Probe.Блокировка гарантирует, что после возврата из подпрограммы все элементы сообщения приняты и расположены в буфере buf.В качестве номера процесса-отправителя можно указать предопределенную константу MPI_ANY_SOURCE - признак того, что подходит сообщение от любого процесса. В качестве идентификатора принимаемого сообщения можно указать константу MPI_ANY_TAG - признак того, что подходит сообщение с любым идентификатором.Если процесс посылает два сообщения другому процессу и оба эти сообщения соответствуют одному и тому же вызову MPI_Recv, то первым будет принято то сообщение, которое было отправлено раньше.
int MPI_Get_count( MPI_Status *status, MPI_Datatype datatype, int *count)
* status - параметры принятого сообщения
* datatype - тип элементов принятого сообщения
* OUT count - число элементов сообщения
По значению параметра status данная подпрограмма определяет число уже принятых (после обращения к MPI_Recv) или принимаемых (после обращения к MPI_Probe или MPI_Iprobe) элементов сообщения типа datatype.
int MPI_Probe( int source, int msgtag, MPI_Comm comm, MPI_Status *status)
* source - номер процесса-отправителя или MPI_ANY_SOURCE
* msgtag - идентификатор ожидаемого сообщения или MPI_ANY_TAG
* comm - идентификатор группы
* OUT status - параметры обнаруженного сообщения
Получение информации о структуре ожидаемого сообщения с блокировкой. Возврата из подпрограммы не произойдет до тех пор, пока сообщение с подходящим идентификатором и номером процесса-отправителя не будет доступно для получения. Атрибуты доступного сообщения можно определить обычным образом с помощью параметра status. Следует обратить внимание, что подпрограмма определяет только факт прихода сообщения, но реально его не принимает.
Прием/передача сообщений без блокировки
int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request)
* buf - адрес начала буфера посылки сообщения
* count - число передаваемых элементов в сообщении
* datatype - тип передаваемых элементов
* dest - номер процесса-получателя
* msgtag - идентификатор сообщения
* comm - идентификатор группы
* OUT request - идентификатор асинхронной передачи
Передача сообщения, аналогичная MPI_Send, однако возврат из подпрограммы происходит сразу после инициализации процесса передачи без ожидания обработки всего сообщения, находящегося в буфере buf. Это означает, что нельзя повторно использовать данный буфер для других целей без получения дополнительной информации о завершении данной посылки. Окончание процесса передачи (т.е. того момента, когда можно переиспользовать буфер buf без опасения испортить передаваемое сообщение) можно определить с помощью параметра request и процедур MPI_Wait и MPI_Test. Сообщение, отправленное любой из процедур MPI_Send и MPI_Isend, может быть принято любой из процедур MPI_Recv и MPI_Irecv.
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request)
* OUT buf - адрес начала буфера приема сообщения
* count - максимальное число элементов в принимаемом сообщении
* datatype - тип элементов принимаемого сообщения
* source - номер процесса-отправителя
* msgtag - идентификатор принимаемого сообщения
* comm - идентификатор группы
* OUT request - идентификатор асинхронного приема сообщения
Прием сообщения, аналогичный MPI_Recv, однако возврат из подпрограммы происходит сразу после инициализации процесса приема без ожидания получения сообщения в буфере buf. Окончание процесса приема можно определить с помощью параметра request и процедур MPI_Wait и MPI_Test.
int MPI_Wait( MPI_Request *request, MPI_Status *status)
* request - идентификатор асинхронного приема или передачи
* OUT status - параметры сообщения
Ожидание завершения асинхронных процедур MPI_Isend или MPI_Irecv, ассоциированных с идентификатором request. В случае приема, атрибуты и длину полученного сообщения можно определить обычным образом с помощью параметра status.
int MPI_Waitall( int count, MPI_Request *requests, MPI_Status *statuses)
* count - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT statuses - параметры сообщений
Выполнение процесса блокируется до тех пор, пока все операции обмена, ассоциированные с указанными идентификаторами, не будут завершены. Если во время одной или нескольких операций обмена возникли ошибки, то поле ошибки в элементах массива statuses будет установлено в соответствующее значение.
int MPI_Waitany( int count, MPI_Request *requests, int *index, MPI_Status *status)
* count - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT index - номер завершенной операции обмена
* OUT status - параметры сообщений
Выполнение процесса блокируется до тех пор, пока какая-либо операция обмена, ассоциированная с указанными идентификаторами, не будет завершена. Если несколько операций могут быть завершены, то случайным образом выбирается одна из них. Параметр index содержит номер элемента в массиве requests, содержащего идентификатор завершенной операции.
int MPI_Waitsome( int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses)
* incount - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT outcount - число идентификаторов завершившихся операций обмена
* OUT indexes - массив номеров завершившихся операции обмена
* OUT statuses - параметры завершившихся сообщений
Выполнение процесса блокируется до тех пор, пока по крайней мере одна из операций обмена, ассоциированных с указанными идентификаторами, не будет завершена. Параметр outcount содержит число завершенных операций, а первые outcount элементов массива indexes содержат номера элементов массива requests с их идентификаторами. Первые outcount элементов массива statuses содержат параметры завершенных операций.
int MPI_Test( MPI_Request *request, int *flag, MPI_Status *status)
* request - идентификатор асинхронного приема или передачи
* OUT flag - признак завершенности операции обмена
* OUT status - параметры сообщения
Проверка завершенности асинхронных процедур MPI_Isend или MPI_Irecv, ассоциированных с идентификатором request. В параметре flag возвращает значение 1, если соответствующая операция завершена, и значение 0 в противном случае. Если завершена процедура приема, то атрибуты и длину полученного сообщения можно определить обычным образом с помощью параметра status.
int MPI_Testall( int count, MPI_Request *requests, int *flag, MPI_Status *statuses)
* count - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT flag - признак завершенности операций обмена
* OUT statuses - параметры сообщений
В параметре flag возвращает значение 1, если все операции, ассоциированные с указанными идентификаторами, завершены (с указанием параметров сообщений в массиве statuses). В противном случае возвращается 0, а элементы массива statuses неопределены.
int MPI_Testany(int count, MPI_Request *requests, int *index, int *flag, MPI_Status *status)
* count - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT index - номер завершенной операции обмена
* OUT flag - признак завершенности операции обмена
* OUT status - параметры сообщения
Если к моменту вызова подпрограммы хотя бы одна из операций обмена завершилась, то в параметре flag возвращается значение 1, index содержит номер соответствующего элемента в массиве requests, а status - параметры сообщения.
int MPI_Testsome( int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses)
* incount - число идентификаторов
* requests - массив идентификаторов асинхронного приема или передачи
* OUT outcount - число идентификаторов завершившихся операций обмена
* OUT indexes - массив номеров завершившихся операции обмена
* OUT statuses - параметры завершившихся операций
Данная подпрограмма работает так же, как и MPI_Waitsome, за исключением того, что возврат происходит немедленно. Если ни одна из указанных операций не завершилась, то значение outcount будет равно нулю.
int MPI_Iprobe( int source, int msgtag, MPI_Comm comm, int *flag, MPI_Status *status)
* source - номер процесса-отправителя или MPI_ANY_SOURCE
* msgtag - идентификатор ожидаемого сообщения или MPI_ANY_TAG
* comm - идентификатор группы
* OUT flag - признак завершенности операции обмена
* OUT status - параметры обнаруженного сообщения
Получение информации о поступлении и структуре ожидаемого сообщения без блокировки. В параметре flag возвращает значение 1, если сообщение с подходящими атрибутами уже может быть принято (в этом случае ее действие полностью аналогично MPI_Probe), и значение 0, если сообщения с указанными атрибутами еще нет.
Объединение запросов на взаимодействие
int MPI_Send_init( void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request)
* buf - адрес начала буфера посылки сообщения
* count - число передаваемых элементов в сообщении
* datatype - тип передаваемых элементов
* dest - номер процесса-получателя
* msgtag - идентификатор сообщения
* comm - идентификатор группы
* OUT request - идентификатор асинхронной передачи
Формирование запроса на выполнение пересылки данных. Все параметры точно такие же, как и у подпрограммы MPI_Isend, однако в отличие от нее пересылка не начинается до вызова подпрограммы MPI_Startall.
int MPI_Recv_init( void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request)
* OUT buf - адрес начала буфера приема сообщения
* count - число принимаемых элементов в сообщении
* datatype - тип принимаемых элементов
* source - номер процесса-отправителя
* msgtag - идентификатор сообщения
* comm - идентификатор группы
* OUT request - идентификатор асинхронного приема
Формирование запроса на выполнение приема данных. Все параметры точно такие же, как и у подпрограммы MPI_Irecv, однако в отличие от нее реальный прием не начинается до вызова подпрограммы MPI_Startall.
MPI_Startall( int count, MPI_Request *requests)
* count - число запросов на взаимодействие
* OUT requests - массив идентификаторов приема/передачи
Запуск всех отложенных взаимодействий, ассоциированных вызовами подпрограмм MPI_Send_init и MPI_Recv_init с элементами массива запросов requests. Все взаимодействия запускаются в режиме без блокировки, а их завершение можно определить обычным образом с помощью процедур MPI_Wait и MPI_Test.
Совмещенные прием/передача сообщений
int MPI_Sendrecv( void *sbuf, int scount, MPI_Datatype stype, int dest, int stag, void *rbuf, int rcount, MPI_Datatype rtype, int source, MPI_Datatype rtag, MPI_Comm comm, MPI_Status *status)
* sbuf - адрес начала буфера посылки сообщения
* scount - число передаваемых элементов в сообщении
* stype - тип передаваемых элементов
* dest - номер процесса-получателя
* stag - идентификатор посылаемого сообщения
* OUT rbuf - адрес начала буфера приема сообщения
* rcount - число принимаемых элементов сообщения
* rtype - тип принимаемых элементов
* source - номер процесса-отправителя
* rtag - идентификатор принимаемого сообщения
* comm - идентификатор группы
* OUT status - параметры принятого сообщения
Коллективные взаимодействия процессов
int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int source, MPI_Comm comm)
* OUT buf - адрес начала буфера посылки сообщения
* count - число передаваемых элементов в сообщении
* datatype - тип передаваемых элементов
* source - номер рассылающего процесса
* comm - идентификатор группы.
int MPI_Gather( void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int dest, MPI_Comm comm)
* sbuf - адрес начала буфера посылки
* scount - число элементов в посылаемом сообщении
* stype - тип элементов отсылаемого сообщения
* OUT rbuf - адрес начала буфера сборки данных
* rcount - число элементов в принимаемом сообщении
* rtype - тип элементов принимаемого сообщения
* dest - номер процесса, на котором происходит сборка данных
* comm - идентификатор группы
* OUT ierror - код ошибки
Сборка данных со всех процессов в буфере rbuf процесса dest. Каждый процесс, включая dest, посылает содержимое своего буфера sbuf процессу dest. Собирающий процесс сохраняет данные в буфере rbuf, располагая их в порядке возрастания номеров процессов. Параметр rbuf имеет значение только на собирающем процессе и на остальных игнорируется, значения параметров count, datatype и dest должны быть одинаковыми у всех процессов.
int MPI_Allreduce( void *sbuf, void *rbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
* sbuf - адрес начала буфера для аргументов
* OUT rbuf - адрес начала буфера для результата
* count - число аргументов у каждого процесса
* datatype - тип аргументов
* op - идентификатор глобальной операции
* comm - идентификатор группы
int MPI_Reduce( void *sbuf, void *rbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
* sbuf - адрес начала буфера для аргументов
* OUT rbuf - адрес начала буфера для результата
* count - число аргументов у каждого процесса
* datatype - тип аргументов
* op - идентификатор глобальной операции
* root - процесс-получатель результата
* comm - идентификатор группы
Функция аналогична предыдущей, но результат будет записан в буфер rbuf только у процесса root.
Синхронизация процессов
int MPI_Barrier( MPI_Comm comm)
* comm - идентификатор группы
Блокирует работу процессов, вызвавших данную процедуру, до тех пор, пока все оставшиеся процессы группы comm также не выполнят эту процедуру.
Работа с группами процессов
int MPI_Comm_split( MPI_Comm comm, int color, int key, MPI_Comm *newcomm)
* comm - идентификатор группы
* color - признак разделения на группы
* key - параметр, определяющий нумерацию в новых группах
* OUT newcomm - идентификатор новой группы
Данная процедура разбивает все множество процессов, входящих в группу comm, на непересекающиеся подгруппы - одну подгруппу на каждое значение параметра color (неотрицательное число). Каждая новая подгруппа содержит все процессы одного цвета. Если в качестве color указано значение MPI_UNDEFINED, то в newcomm будет возвращено значение MPI_COMM_NULL.
int MPI_Comm_free( MPI_Comm comm)
* OUT comm - идентификатор группы
Уничтожает группу, ассоциированную с идентификатором comm, который после возвращения устанавливается в MPI_COMM_NULL.
Предопределенные константы
Предопределенные константы типа элементов сообщений
Константы MPI Тип в C
MPI_CHAR signed char
MPI_SHORT signed int
MPI_INT signed int
MPI_LONG signed long int
MPI_UNSIGNED_CHAR unsigned char
MPI_UNSIGNED_SHORT unsigned int
MPI_UNSIGNED unsigned int
MPI_UNSIGNED_LONG unsigned long int
MPI_FLOAT float
MPI_DOUBLE double
MPI_LONG_DOUBLE long double
Другие предопределенные типы
MPI_Status - структура; атрибуты сообщений; содержит три обязательных поля:
* MPI_Source (номер процесса отправителя)
* MPI_Tag (идентификатор сообщения)
* MPI_Error (код ошибки)
MPI_Request - системный тип; идентификатор операции посылки-приема сообщения
MPI_Comm - системный тип; идентификатор группы (коммуникатора)
MPI_COMM_WORLD - зарезервированный идентификатор группы, состоящей их всех процессов приложения
Константы-пустышки
* MPI_COMM_NULL
* MPI_DATATYPE_NULL
* MPI_REQUEST_NULL
Константа неопределенного значения
* MPI_UNDEFINED
Глобальные операции
MPI_MAX
MPI_MIN
MPI_SUM
MPI_PROD
Любой процесс/идентификатор
MPI_ANY_SOURCE
MPI_ANY_TAG
Код успешного завершения процедуры
MPI_SUCCESS
2. Задача о ранце
2.1 Постановка задачи о ранце
Задача о ранце -- одна из задач комбинаторной оптимизации. Название это получила от максимизационной задачи укладки как можно большего числа нужных вещей в рюкзак при условии, что общий объём (или вес) всех предметов ограничен. Подобные задачи часто возникают в экономике, прикладной математике, криптографии. В общем виде, задачу можно сформулировать так: из неограниченного множества предметов со свойствами «стоимость» и «вес», требуется отобрать некое число предметов таким образом, чтобы получить максимальную суммарную стоимость при одновременном соблюдении ограничения на суммарный вес.
2.2 Алгоритм задачи о ранце
Пусть имеется n предметов, каждый из которых имеет ценность >0 и вес >0, j = 1,2,3,…..,n. Имеется ранец, грузоподъемность которого есть R, при этом >R, т.е. все предметы в ранец положить невозможно. Необходимо положить в ранец набор предметов с максимальной суммарной ценностью. Введем n переменных:
={0,1},
0 = если предмет с номером j не кладется в ранец,
1 = если предмет с номером j кладется в ранец,
j = 1,2,…….,n.
После введения этих переменных суммарная ценность и грузоподъемность соответственна имеют вид:
= ,
= .
Поэтому задача о ранце имеет вид:
(3.2.1)
, (3.2.2)
(3.2.3)
Естественно предположить, что > 0, 0 < < R, j = 1,2,... ,n. Множество допустимых решений этой задачи -- это множество n -мерных булевых векторов х = (,,...,), удовлетворяющих условию (3.2.2). Вместе с задачей (3.2.1)-(3.2.3) будем рассматривать задачу линейного программирования, в которой вместо условий (3.2.3) вводятся условия
0<<1, j = l,2,...,n. (3.2.3')
2.3 Задача о многомерном ранце
(3.2.4)
(3.2.5)
(3.2.6)
Предполагаем, что > 0, 0 < <, i= 1, 2,..., n, j = 1, 2,...,n.
В этой задаче каждый предмет имеет т свойств (кроме ценности). Эти свойства описываются количественно с помощью элементов столбца с номером j матрицы . Множество допустимых решений этой задачи -- это множество булевых векторов , удовлетворяющих условиям (3.2.5). Вместе с задачей (3.2.4)-(3.2.6) будем рассматривать задачу линейного программирования, в которой вместо условий (3.2.6) вводятся условия
0<<1, j = l,2,...,n. (3.2.6')
Экономическая интерпретация. Пусть имеется п проектов, и для их реализации задан вектор ресурсов b , > 0. Обозначим через > 0 количество единиц ресурса типа i, необходимое для реализации проекта с номером j, при этом для любого ресурса s выполнено условие , т.е. реализация всех проектов невозможна. Пусть > 0, j = 1, 2,...,n -- прибыль, полученная при реализации проекта j. Требуется выбрать для реализации набор проектов с максимальной суммарной прибылью. Введем п булевых переменных:
=0 (если проект j не реализуется),
=1 (если проект j реализуется),
j=1,2,3,…..,n.
Получим задачу о многомерном ранце (3.2.4)-(3.2.6).
Общие свойства задач о ранце
Существование допустимого решения. В задачах (3.2.1)-(3.2.3) и (3.2.4)-(3.2.6) допустимое решение всегда существует (например, нулевое). Если решить задачи линейного программирования (3.2.1)- (3.2.3;) и (3.2.4)-(3.2.6/) и в полученных оптимальных решениях заменить дробные значения нулевыми, то получим допустимые решения задач (3.2.1)-(3.2.3) и (3.2.4)-(3.2.6). Число дробных значений в оптимальных решениях линейных задач не превосходит т.
Отсев подмножеств булевых векторов, не содержащих оптимальных решений. Рассмотрим вектор, имеющий к единиц и n -- к нулей. Если вектор х является допустимым (т.е. условия (3.2.2) или (3.2.5) выполнены), то можно исключить из дальнейшего рассмотрения 2к векторов, получающихся из х заменой любого числа единиц нулями, так как для любого такого вектора х значение функции f(x) будет меньше, чем . Если вектор х не является допустимым (т.е. условия (2.2.2) или (2.2.5) не выполняются), то можно исключить из дальнейшего рассмотрения векторов, которые получаются из х заменой любого числа нулей единицами, так как все эти векторы недопустимы.
Оценка точности приближенных булевых решений. Пусть -- допустимое булево решение задачи (3.2.1)-(3.2.3) или (3.2.4)--(3.2.6); -- оптимальное булево решение; -- оптимальное решение задачи (3.2.1)- (3.2.3') или (3.2.4)-(3.2.6/). Тогда имеет место следующее неравенство:
(3.2.7)
Величина есть гарантированная оценка отклонения приближенного решения от оптимума . Нахождение вектора связано с решением задачи линейного программирования. Далее в п. 3.2.4 показано, что задача (3.2.1)-(3.2.3/) решается просто. Для решения задачи (3.2.4)-(3.2.6/) необходимо применение симплексного алгоритма, что не всегда удобно. Для задачи (3.2.4)-(3.2.6) можно получить более грубую оценку, решив последовательность из т задач вида (3.2.1)-(3.2.3/). Пусть -- значение целевой функции для оптимального решения линейной задачи, полученной из задачи (3.2.4)-(3.2.6/) с учетом лишь одного ограничения с номером i. Тогда вместо неравенства (3.2.7) получим
(2.2.8)
В этом случае гарантированная оценка отклонения приближенного решения от оптимума имеет вид:
.
Алгоритм динамического программирования решения задачи о ранце
Мы решаем задачу с помощью реккурентного соотношения, называемого уравнением Беллмана.
j = 1,2,…….,n ; i = 1,2,……., max.
Решение задачи о ранце на С++
Пусть уравнение имеется вид,
Получается 6 предметов(obj), и максималный вес(max_w) имеет ценность 15.
Obj |
1 |
2 |
3 |
4 |
5 |
6 |
|
P |
3 |
6 |
7 |
9 |
11 |
18 |
|
W |
1 |
2 |
3 |
5 |
6 |
8 |
Решением является вектор из нулей и единиц, задающий значения переменных.
Прямой ход алгоритма динамического программирования решения задачи о ранце
В таблице столбцы обозначают вес от нуля до максимального значения (т.е Грузоподъемность), а строки обозначают количество предметов. Прямой ход алгоритма динамического программирования решения задачи о ранце использует уравнение Беллмана для вычисления очередного значения в ячейке таблицы.
Заполняется столбец сверху вниз.
В данном случае сравнивается 16 и 16 + 9 = 25. Т.к. как 25 больше чем 16, то в ячейку записывается 25.
Максимальное значение целевой функции оказывается в правом нижнем углу.
Обратный ход алгоритма динамического программирования решения задачи о ранце
Обратный ход позволяет восстановить значения переменных найденного решения по таблице.
Проверка результата
Проверяем, что полученное решение удовлетворяет ограничению по весу: суммарный вес предметов равен 14, что меньше 15. Суммарная стоимость предметов 34, что соответствует значению в таблице. Таким образом ответ верен.
Программный код на С + +
1. #include<iostream>
2. #include<conio.h>
3. #include<stdio.h>
4. #include<stdlib.h>
5. #include<iomanip>
6. using namespace std;
7. #define max_w 15
8. #define obj 6
9. void main()
10.{
11. int a[max_w+1][obj],p[6],w[6],temp=0,k=0,x[obj];
12. p[0]=3;p[1]=6;p[2]=7;p[3]=9;p[4]=11;p[5]=18;
13. w[0]=1;w[1]=2;w[2]=3;w[3]=5;w[4]=6;w[5]=8;
14. for(int j=0;j<obj;j++)
15. {
16. for(int i=0;i<max_w+1;i++)
17. {
18. if(i<w[j])
19. {
20. if(i==0 || (j-1)<0)a[i][j]=0;
21. else a[i][j]=a[i][j-1];
22. }
23. if(i>=w[j])
24. {
25. if((j-1)<0)a[i][j]=p[j];
26. else
27. {
28. if(a[i][j-1]>(a[k][j-32.1]+p[j]))a[i][j]=a[i][j-1];
29. else a[i][j]=a[k][j-1]+p[j];
30. k++;
31. }
32. }
33. }
34. k=0;
35. }
36. for(int i=0;i<max_w+1;i++)
37. {
38. for(int j=0;j<obj;j++)
39. {
40. cout<<setw(4)<<a[i][j]<<" ";
41. }
42. cout<<endl;
43. }
44. k=max_w;
45. cout<<"\nThe Answer is = ";
46. for(int i=obj-1;i>=0;i--)
47. {
48. if(a[k][i]!=a[k][i-1])
49. {
50. x[i]=1;k=k-w[i];
51. }
52. else if(i==0 && a[k][i]!=0)x[i]=1;
53. else
54. x[i]=0;
55. }
56. for(int i=0;i<obj;i++)cout<<x[i]<<" ";
57. cout<<"\n\nThe maximum value is = "<<a[max_w][obj-1];
58. _getch();
59. }
Прямой ход на (С ++)
for(int j=0;j<obj;j++){
for(int i=0;i<max_w+1;i++){
if(i<w[j]){
if(i==0 || (j-1)<0)a[i][j]=0;
else a[i][j]=a[i][j-1];
}
if(i>=w[j]){
if((j-1)<0)a[i][j]=p[j];
else{
if(a[i][j-1]>(a[k][j-1]+p[j]))a[i][j]=a[i][j-1];
else a[i][j]=a[k][j-1]+p[j];
k++;
}
}
}
k=0;
}
Обратный ход на (С++)
for(int i=obj-1;i>=0;i--)
{
if(a[k][i]!=a[k][i-1])
{
x[i]=1;k=k-w[i];
}
else if(i==0 && a[k][i]!=0)x[i]=1;
else
x[i]=0;
}
Результат работы программы решения задачи о ранце на С++
Рис(1). Результат программы задача о ранце на С++
На Рис(1) представлен результат программы задачи о ранце на С++.
3. Задача о ранце на OpenMP
Для параллельной реализации применялась технология OpenMP. OpenMP является реализацией многопоточности. На параллельных участках мастер-тред разделяется на определенное количество подчинённых тредов и задача распределяется между ними.
Распараллеливается основной цикл прямого хода с помощью дерективы parallel for. В результате итерации цикла распределяются между параллельными потоками.
3.1 Платформа для экспериментов
Эксперименты выполнялись c n=5000 переменными на компьютере с двумя процессорами IntelCore 2 Quad E5335 2,00 ГГц и 8 Гб памяти.
3.2 Решение задачи о ранце на OpenMP
01. #include<omp.h>
02. #include<iostream>
03. #include<stdlib.h>
04. #include<stdio.h>
05. #include<math.h>
06. #include<fstream>
07. #include<iomanip>
08. using namespace std;
09. class KnapSolver
10. {
11. int *a,*p,*w,*x,max_w,obj;
12. public:
13. void read(char *file_name);
14. void solve();
15. void output();
16. };
17. void KnapSolver::read(char *file_name)
18. {
19. ifstream g;
20. int t=0;
21. g.open(file_name);
22. if(!g)
23. {
24. cerr << "Error: file could not be opened" << endl;
25. exit(1);
26. }
27. g >> obj;
28. g >> max_w;
29. w=(int *)malloc(obj*sizeof(int));
30. if(w == NULL){cerr<<"Error : Your size is too much.\n"; 31. exit(1);}
32. p=(int *)malloc(obj*sizeof(int));
33. if(p == NULL){cerr<<"Error : Your size is too much.\n"; 34. exit(1);}
35. while(!g.eof())
36. {
37. g >> p[t];
38. g >> w[t];
39. t++;
40. if(t > obj)break;
41. }
42. printf("\nTotal number of object is %d.\n\n",obj);
43. printf("The maximum weight is %d.\n\n",max_w);
44. }
45. void KnapSolver::solve()
46. {
47. int i,j,chunk=10;
48. a=(int *)malloc((max_w+1)*obj*sizeof(int));
49. if(a == NULL){cerr<<"Error : Your size is too much.\n"; 50. exit(1);}
51. x=(int *)malloc(obj*sizeof(int));
52. if(x == NULL){cerr<<"Error : Your size is too much.\n"; 53. exit(1);}
54. {
55. for(j=0;j<obj;j++)
56. {
57. #pragma omp parallel for
58. for(i=0;i<max_w+1;i++)
59. {
60. if(i<w[j])
61. {
62. if(i==0 || j == 0)a[i*obj+j]=0;
63. else
64. a[i*obj+j]=a[i*obj+j-1];
65. }
66. if(i>=w[j])
67. {
68. if(j == 0)
69. a[i*obj+j]=p[j];
70. else
71. {
72. int k=i-w[j];
73. if(a[i*obj+j-1]>(a[k*obj+j-1]+p[j]))
74. a[i*obj+j]=a[i*obj+j-1];
75. else
76. a[i*obj+j]=a[k*obj+j-1]+p[j];
77. }
78. }
79. }
80. }
81. }
82. int k=max_w;
83. for(int i=obj-1;i>=0;i--)
84. {
85. if(i==0)
86. {
87. if(a[k*obj]==0)x[i]=0;
88. else
89. x[i] = 1;
90. }
91. else if(a[k*obj+i]!=a[k*obj+i-1])
92. {
93. x[i]=1;k=k-w[i];
94. }
95. else
96. x[i]=0;
97. }
98. }
99. void KnapSolver::output()
100. {
101. #if 0 for(int j = 0; j < max_w + 1; j ++)
102. {
103. for(int i = 0; i < obj; i ++)
104. {
105. printf("%d ", a[j * obj + i]);
106. }
107. printf("\n");
108. }
109. #endif cout<<"\nThe Answer is = ";
110. /*for(int i=0;i<max_w+1;i++)
111. {
112. for(int j=0;j<obj;j++)
113. {cout<<setw(4)<<a[i*obj+j]<<" ";}
114. cout<<endl;
115. }*/
116. for(int i=0;i<obj;i++)cout<<x[i]<<" ";
117. cout<<"\n\nThe maximum value is = "<<a[max_w*obj+obj-1] 118. <<endl;
119. }
120. int main(int argc, char* argv[])
121. {
122. KnapSolver kp;
123. char* str = NULL;
124. int nt = 1;
125. double start=0,end=0;
126. if(argc >= 2)
127. {
128. str = argv[1];
129. if(argc == 3)
130. {
131. nt = atoi(argv[2]);
132. }
133. }
134. else
135. {
136. fprintf(stderr,"usage:%s<input_file><number_of_threads>\n", 137. argv[0]);
138. exit(-1);
139. }
140. omp_set_num_threads(nt);
141. start = omp_get_wtime();
142. kp.read(str);
143. kp.solve();
144. kp.output();
145. end = omp_get_wtime();
146. printf("\nRuntime = %f seconds.\n\n",end-start);
147. }
В программе параллельно решена задача о ранце с классом который называется (KnapSolver).В функции “read(char *file_name)” класса читаются параметры из файла. В функции solve() класса решена задача. В этом функции от строки 53 до 81 - это параллельное пространство.
3.3 Списковый алгоритм решения задачи о ранце
1. #include<iostream>
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<iomanip>
5. #define n 12
6. #define max_w 15
7. using namespace std;
8. int main()
9. {
10. int w[6],p[6],mat_w[max_w+1][n+1],mat_p[max_w+1][n+1];
11. int k=0,count=0,col_count[n+1],def=0,max_ans=0;
12. w[0]=1;w[1]=2;w[2]=3;w[3]=5;w[4]=6;w[5]=8;
13. p[0]=3;p[1]=6;p[2]=7;p[3]=9;p[4]=11;p[5]=18;
14. for(int i=0;i<max_w+1;i++)
15. for(int j=0;j<n+1;j++){
16. mat_w[i][j]=0;
17. mat_p[i][j]=0;
18. }
19. for(int j=0;j<n+1;j++){
20. if(j==0){
21. mat_w[0][j]=0;
22. mat_p[0][j]=0;
23. count++;
24. col_count[j]=count;
25. }
26. if(j>0 && j%2!=0){
27. for(int i=0;i<col_count[j-1];i++){
28. if(mat_w[i][j-1]+w[k]<=max_w){
29. mat_w[i][j]=mat_w[i][j-1]+w[k];
30. mat_p[i][j]=mat_p[i][j-1]+p[k];
31. count++;
32. col_count[j]=count;
33. if(mat_p[i][j]>max_ans)
34. max_ans=mat_p[i][j];
35. }
36. }
37. k++;
38. }
39. if(j>0 && j%2==0){
40. for(int i=0;i<col_count[j-1]+col_count[j-2];i++){
41. if(i<col_count[j-2]){
42. mat_w[i][j]=mat_w[i][j-2];
43. mat_p[i][j]=mat_p[i][j-2];
44. count++;
45. }
46. if(i>=col_count[j-2]){
47. if(mat_w[i-col_count[j-2]][j-1] < i){
48. def=i-mat_w[i-col_count[j-2]][j-1];
49. if(mat_p[i-def][j]<mat_p[i-col_count[j-2]][j-1]){
50. mat_w[i-def][j]=mat_w[i-col_count[j-2]][j-1];
51. mat_p[i-def][j]=mat_p[i-col_count[j-2]][j-1];
52. count++;
53. }
54. }
55. else
56. {
57. mat_w[i][j]=mat_w[i-col_count[j-2]][j-1];
58. mat_p[i][j]=mat_p[i-col_count[j-2]][j-1];
59. count++;
60. }
61. }
62. col_count[j]=count;
63. }
64. }
65. count=0;
66. }
67. for(int i=0;i<max_w+1;i++){
68. for(int j=0;j<n+1;j++){
69. cout<<setw(2)<<mat_w[i][j]<<" ,"<<setw(2)<<mat_p[i][j]<<" ";
70. }
71. cout<<endl;
72. }
73. cout<<endl;
74. cout<<"The Maximun Possibality is = "<<max_ans;
75. //for(int i=0;i<n+1;i++)cout<<col_count[i]<<" ";
76. cout<<endl<<endl;
77. }
График зависимости времени решения от числа потоков
Это график зависимости времени решения от числа потоков. Мы видим, что чем больше потоков, тем меньше время решения.
Ускорение
Здесь показан график ускорения. Видно, что чем больше количество тредов, тем выше ускорение. Графики для ускорения и времени решения демонстрируют эффективность применения параллельных вычислений.
Заключение
Результаты проделанной работы:
· Реализован алгоритм динамического программирования для решения задачи о ранце в последовательном и параллельном (Open MP) варианте.
· Проведен вычислительный эксперимент, который показал, что применение OpenMP позволяет существенно ускорить процесс решения задачи.
Также реализован так называемый «списковый» алгоритм динамического программированиях[8]. Для большинства задач о ранце «списковый» вариант работает быстрее, чем обычный. В будущих исследованиях планируется распараллелить также этот вариант и провести масштабный вычислительный эксперимент.
4. Список используемой литературы
1. С.А. Лупин, М.А. Посыпкин. Технологии параллельного программирования.
2. http://openmp.org
3.(http://www.parallel.ru/computers) Технологии паралельного програмирования .
4. (http://www.parallel.ru/computers) источники информации.
5.(http://www.parallel.ru/computers) Основные классы современных параллельных компьютеров.
6. Сигал И.Х., Иванова А.П. Введение в прикладное дискретное программирование: модели и вычислительные алгоритмы.
7. www.lam-mpi.org.
8. Kellerer H., Pfershy U., Pisinger D. Knapsack Problems.-Springer Verlag, 2004-546 p.
Размещено на Allbest
Подобные документы
Задача о ранце как задача комбинаторной оптимизации. Задача о загрузке, рюкзаке, ранце. Постановка и NP-полнота задачи. Классификация методов решения задачи о рюкзаке. Динамическое программирование. Метод ветвей и границ. Сравнительный анализ методов.
курсовая работа [1,7 M], добавлен 18.01.2011Методы решения задачи о ранце. Алгоритм неявного лексикографического перебора. Разработка структуры данных, реализация алгоритма с её использованием, программная реализация. Проведение тестовой проверки. Входной и выходной файл, листинг программы.
курсовая работа [408,8 K], добавлен 22.10.2012Изучение средств распараллеливания, предоставляемых технологиями OpenMP. Исследование синтаксиса и семантики функций технологии OpenMP на языке программирования Visual C++). Проектирование интерфейса пользователя для взаимодействия с программой.
контрольная работа [773,9 K], добавлен 12.07.2015Технология разработки параллельных программ для многопроцессорных вычислительных систем с общей памятью. Синтаксис, семантика и структура модели OpenMP: директивы, процедуры и переменные окружения. Распараллеливание по данным и операциям, синхронизация.
презентация [1,2 M], добавлен 10.02.2014Анализ средств распараллеливания, предоставляемых технологиями OpenMP. Синтаксис и семантика функций технологии OpenMP на языке программирования Visual C++. Компиляция программы, проектирование интерфейса пользователя для взаимодействия с программой.
курсовая работа [492,0 K], добавлен 09.08.2015Теоретическая основа линейного программирования. Задачи линейного программирования, методы решения. Анализ оптимального решения. Решение одноиндексной задачи линейного программирования. Постановка задачи и ввод данных. Построение модели и этапы решения.
курсовая работа [132,0 K], добавлен 09.12.2008Технологія OpenMP як найпопулярніший засіб програмування комп'ютерів із загальною пам'яттю. Типи конструкцій OpenMP: функції виконуючого середовища OpenMP, директиви pragma. Аналіз параметрів операційного середовища OpenMP, особливості типів блокувань.
реферат [397,2 K], добавлен 09.06.2012Решение задачи линейного программирования симплекс-методом: постановка задачи, построение экономико-математической модели. Решение транспортной задачи методом потенциалов: построение исходного опорного плана, определение его оптимального значения.
контрольная работа [118,5 K], добавлен 11.04.2012Математическое программирование. Линейное программирование. Задачи линейного программирования. Графический метод решения задачи линейного программирования. Экономическая постановка задачи линейного программирования. Построение математической модели.
курсовая работа [581,5 K], добавлен 13.10.2008Сущность и постановка транспортной задачи для n переменных, их виды, применение и пример решения в MS Excel. Управляющие структуры ветвления Maple языка (if предложение). Решение транспортной задачи в векторных координатах для двух и трёх матриц.
дипломная работа [109,3 K], добавлен 12.01.2011