Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 24

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ УКРАЇНИ


«КИЇВСЬКИЙ ПОЛІТЕХНІЧНИЙ ІНСТИТУТ
ІМЕНІ ІГОРЯ СІКОРСЬКОГО»

Кафедра прикладної математики

КУРСОВА РОБОТА
З дисципліни «Програмування»
На тему:
Задача про рюкзак

Виконав:
Студент I курсу групи КМ-
13, спеціальність 113
Прикладна математика
БОБРИШЕВ О.М

Керівник:
ЛЮБАШЕНКО Н.Д.

Оцінка:_________________
Кількість балів:__________

Київ - 2022
1

ЗМІСТ:

Оглавление
ВСТУП.....................................................................................................................................................2
1 ПОСТАНОВКА ЗАВДАННЯ..................................................................................................................3
2 Вибір та опис методу розв’язання....................................................................................................4
3 Алгоритм............................................................................................................................................6
4 Опис програми...................................................................................................................................7
4.1 Інструкція для кінцевого користувача......................................................................................7
4.2 Інструкція для програміста........................................................................................................9
4.2.1 Структура програми............................................................................................................9
4.2.2 Основні фунціональні модулі...........................................................................................10
4.2.3 Основні дані.......................................................................................................................11
5 Результати виконання тестів...........................................................................................................13
ВИСНОВКИ...........................................................................................................................................15
Література...........................................................................................................................................16
Додаток А. Текст програми на мові С................................................................................................17
2

ВСТУП

Проблематика

Дано предмети, які мають вагу та коштовність, та максимальну вагу,


яку може витримати рюкзак. Потрібно взяти у рюкзак предмети так, щоб
сумарна вага не перевищувала задану, а коштовність цієї множини
предметів була найбільшою.

Об’єкт дослідження – предмети.

Предмет дослідження – фінальний вибір предметів.

Мета курсової роботи

Мета: дослідження алгоритмів розв’язку задачі; обрання


оптимального алгоритму; розробка програмного продукту на мові С;
розгляд можливостей практичного використання розробленого
програмного продукту.
3

1 ПОСТАНОВКА ЗАВДАННЯ

Дано n предметів з вагами w 1 , .. , wn та коштовностями v 1 , .. , v n, рюкзак,


який може витримати максимальну вагу W. При цьому W > 0,
v i> 0, w i> 0, де ⅈ ∈ [ 0 , n ]. Потрібно знайти підмножину предметів, яку можна
помістити у рюкзак, та яка буде мати максимальну коштовність.
4

2 Вибір та опис методу розв’язання

Для розв’язання задачі про рюкзак існує 4 основні алгоритми


розв’язку. Точними методами розв’язку є метод вичерпного перебору,
метод гілок та меж, метод динамічного програмування. До наближених
методів розв’язку відноситься жадібний алгоритм.
Жадібний алгоритм. Для розв’язку задачі жадібним алгоритмом
потрібно відсортувати предмети в залежності від їх питомою цінності.
Обирати предмети в рюкзак потрібно від предмету з найбільшою питомою
цінністю до предмету з найменшою до тих пір, поки вага множини цих
предметів не буде більшою за максимальну вагу, яку може витримати
рюкзак. В залежності від вхідних даних, жадібний алгоритм може видавати
результат як завгодно далекий від оптимального.
Вичерпний перебір. Вичерпний перебір в цій задачі призводить до
генерації та розгляданню усіх підмножин з n предметів, визначення ваги та
коштовності кожної з них, з’ясування, чи є допустимим такий набір
предметів за вагою, обрання з допустимих підмножин ту, яка має
найбільшу сумарну коштовність. Загальна кількість підмножин n-
елементної множини дорівнює 2 , тому вичерпний перебір призводить до
n

алгоритму з часом виконання Ω ( 2n ) незалежно від обраного методу


генерації підмножин.
Метод гілок та меж. Цей метод є узагальненням вичерпного
перебору. Для цього методу потрібно упорядкувати предмети у спадному
порядку за їх питомою цінністю:
v1 v2 vn
≥ ≥…≥
w1 w2 wn

Далі потрібно створити бінарне дерево. Кожен вузол дерева на рівні


0 ≤ ⅈ ≤ n являє собою підмножину з n елементів, які містять деякий набір з
певної кількості перших i елементів. Для перших i елементів ліва гілка
вказує на те, що наступний елемент входить у підмножину, у той час як
права вказує на відсутність елемента. Усім вузлам записуються загальні
значення коштовності v i, ваги w i та верхньої межі значення підмножин
v i+1
vb=v +(W −w) , яка може бути отримана при додаванні елементів до
wi +1
цього вузла. У найгіршому випадку вхідних даних задачі, складність
алгоритму становить O ( 2n ).
5

Метод динамічного програмування. Для розробки алгоритму


необхідно вивести рекурентну формулу, яка визначає розв’язок екземпляру
задачі. Розглянемо екземпляр, який визначається першими i предметами,
де 1 ≤ ⅈ ≤n , з коштовностями v 1 , .. , v i, вагами w 1 , … , w i, та максимальною
вагою, яку може витримати рюкзак 1 ≤ j≤ W . Нехай V[i, j] – коштовність
найбільш цінної підмножини з i предметів ємністю рюкзака j. У
підмножину з i предметів може входити i-ий предмет або не входити.
Якщо він не входить, то V[i, j] = V[i-1, j], якщо входить, то V[i, j] = v i +
V[i-1, j]. Таким чином можна записати рекурентну формулу:

{
V [ i−1 , j ] = max {V [ i−1 , j ] , v i +V [ i−1 , j ] } , якщо j−w i> 0 З початковими умовами
V [ i−1 , j ] , якщо j−w i <0
V [ 0 , j ] =0 , V [ i , 0 ] =0 , при j ≥ 0 ,i ≥ 0.

За цією формулою можна заповнити матрицю розмірністю n+1×w+1 (див.


рисунок 2.1).

Рисунок 2.1 – матриця динамічного програмування

Для того, щоб дізнатись набор предметів, який має входити у рюкзак,
необхідно порівняти значення V[n, w] з V[n-1,w]. Якщо вони рівні, то n
елемент не входить у рюкзак, якщо ні, то входить. Після цього потрібно
перевіряти V[n-1, w−wn] з V[n-2, w−wn] і так далі.
Цей алгоритм є псевдо-поліноміальним з часовою ефективністю θ ( nw )
. Через часову ефективність методом розв’язку задачі було обрано
алгоритм динамічного програмування.
6

3 Алгоритм

Псевдокод побудови матриці:

for i := 0 to n

for j := 0 to W

if i = 0 to j = 0

v[i, j] := 0

else if j < w i

v[i, j] := v[i-1, j]

else

v[i, j] := max(v[i-1, j], vi + v[i-1, j - w i])

end if

end for

end for

Псевдокод знаходження фінального набору предметів:

for i := n to 0

if v[i-1, w n] = v[i-1, w n]

continuew

else

додання i-ого елементу до відповіді


w n = w n - w n−1

end if

end for
7

4 Опис програми

4.1 Інструкція для кінцевого користувача

На першому етапі роботи програми, відображається обкладинка


роботи (див. рисунок 4.1). Для того, щоб перейти в меню, необхідно
натиснути будь-яку кнопку на клавіатурі.

Рисунок 4.1.1 – обкладинка програми

Після натиснення кнопки відображається меню. Для початку


роботи програми необхідно натиснути кнопку 1. Для повернення до
обкладинки – 2. Натиснення будь-якої іншої кнопки призводить до
завершення роботи програми (див. рисунок 4.2).
8

Рисунок 4.1.2 – меню програми

Після натиснення одиниці, потрібно ввести максимальну вагу, яку


може витримати рюкзак. Потім – параметри предметів (назва, вага,
коштовність). Після введення необхідних даних, буде виведено
результат. Потім користувачу буде запропоновано обрати один з
варіантів: очистити консоль вводу натисненням одиниці або
продовження виконання натисненням будь-якої іншої кнопки (див
рисунок 4.3).

Рисунок 4.1.3 – введення даних


9

4.2 Інструкція для програміста

4.2.1 Структура програми


10

4.2.2 Основні фунціональні модулі

Таблиця 1 – Опис основних функцій

Назва Параметри Призначення (результат


функції роботи)

Переміщення курсору в
консолі у точку з
gotoxy x – координата х, координатами
y – координата y x, y

intro - Створення обкладинки


програми

Перевірка потоку введення на


зайві елементи після
check_stdin isLast – позиція зчитування значень параметрів
елемента у введенні. функцією scanf

isLast – позиція Перевірка на те, чи були


елемента у введенні. значення записані в змінні та
check_value на те, чи не залишились зайві
a - значення, значення у потоці
повернене функцією
scanf після
зчитування

program - Код алгоритму програми


11

4.2.3 Основні дані

Таблиця 2 – Опис основних даних

Назви об’єктів Опис Призначення

збереження параметрів
введених предметів
items Тип - струтура

Об’єкти

a, b, c, d, e, f Тип – змінна int Символи з кодами ASCII

Значення переміщення
рамки від лівого краю
sh Тип - змінна int консолі

Значення того, чи є
останній зчитаний елемент
result Тип - змінна int симоволом пробілу або
переведення рядка

Зчитаний символу з потоку


введення
ch Тип – змінна char

Дорівнює поверненому
значенню функції
isEmpty Тип – змінна int check_stdin

Збереження значень
структур
items inf Тип – масив struct

Лічильник для створення


циклу
i Тип – змінна int
12

Кількість предметів

len Тип – змінна int

isNormal_weitght, Перевірка введених


параметрів предметів
isNormal_value, Тип – змінна int

max_weight_check

Створення динамічного
масиву в залежності від
result Тип – вказівник на значення len
int

Матриця коштовностей
екземплярів задачі
V Тип – масив int

Лічильник кількості
предметів, які входять у
k Тип – змінна int склад фінальної
оптимальної підмножини
13

5 Результати виконання тестів

1. Максимальна вага – 10

1) Назва – 1, вага – 7, цінність – 42


2) Назва – 2, вага – 3, цінність – 12
3) Назва – 3, вага – 4, цінність – 40
4) Назва – 4, вага – 5, цінність – 25

Таблиця динамічного програмування

2. Максимальна вага – 8

1) Назва – Книга, вага – 1, цінність – 600


2) Назва – Бінокль, вага – 2, цінність – 5000
3) Назва – Аптечка, вага – 4, цінність – 1500
4) Назва – Ноутбук, вага – 2, цінність – 40000
5) Назва – Чайник, вага – 1, цінність – 500
14
15

ВИСНОВКИ

Під час виконання курсової роботи було досліджено алгоритми


розв’язку поставленої задачі, обрано оптимальний з них, було
розроблено програмний продукт на мові С.

Перевагою обраного алгоритму є те, що він розв’язує задачу за


псевдо-поліноміальний час, який залежить від ваги рюкзака. Алгоритм
гарно себе проявляю в задачах з W ≤ 10000. Недоліком алгоритму є те, що
для задання максимальної ваги і ваги предметів можна використовувати
лише цілі числа. Шляхів подальшого покращення у даного алгоритму не
існує.

Колом потенційних користувачів можуть бути люди, які


займаються розподілом ресурсів, логістикою, наприклад, для
знаходження оптимальною загрузки транспортних засобів; також задача
може бути застосована в математиці, інформатиці, криптографії та
інших областях науки.
16

Література

1. Левитин А. Алгоритмы: введение в разработку и анализ. : Пер.


с англ. / Ананий В. Левитин,— М. : Издательский дом
“Вильямс”, 2006. — 576 с. : ил. — Парал. тит. англ. ISBN 5-
8459-0987-2 (рус.)
https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B4%D0%B
0%D1%87%D0%B0_%D0%BE_%D1%80%D1%8E%D0%BA
%D0%B7%D0%B0%D0%BA%D0%B5#
%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9_
%D0%B0%D0%BB%D0%B3%D0%BE
%D1%80%D0%B8%D1%82%D0%BC
2. https://elib.bsu.by/bitstream/123456789/240021/1/%D0%9A
%D0%B0%D1%80%D0%B0%D0%BD%D0%BA
%D0%B5%D0%B2%D0%B8%D1%87_%D0%AF
%D1%89%D0%B8%D0%BA%D0%BE
%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_
%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0%20%D
0%BE%20%D1%80%D1%8E%D0%BA
%D0%B7%D0%B0%D0%BA
%D0%B5%20%D0%B8%20%D0%B5%D0%B5%20%D0%BF
%D1%80%D0%B8%D0%BC%D0%B5%D0%BD
%D0%B5%D0%BD
%D0%B8%D0%B5%20%D0%B2%20%D0%BB%D0%BE
%D0%B3%D0%B8%D1%81%D1%82%D0%B8%D0%BA
%D0%B5.pdf
17

Додаток А. Текст програми на мові С

#include <stdio.h>
#include <conio.h>
#include <windows.h>

struct items{ // створення структури предметів


char name[30];
int weight;
float value;
};

void gotoxy(int x, int y)


{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void intro(){ // функція створення обкладинки програми

SetConsoleCP(866);
SetConsoleOutputCP(866);

int a = 218; // лівий верхній кут рамки


int b = 196; // горизонтальная лінія
int c = 191; // правий верхній кут рамки
int d = 179; // вертикальна лінія
int e = 192; // лівий нижній кут
int f = 217; // правий нижній кут

int sh = 23;
gotoxy(sh, 1);
18

printf("%c", a);
for (int i = 0; i < 70; i++){
printf("%c", b);
}
printf("%c\n", c);

for (int i = 0; i < 15; i++){


gotoxy(sh, i+2);
printf("%c", d);
for (int j = 0; j < 70; j++){
printf(" ");
}
printf("%c\n", d);
}

gotoxy(sh, 17);
printf("%c", e);
for (int i = 0; i < 70; i++){
printf("%c", b);
}
printf("%c\n", f);

SetConsoleCP(1251);
SetConsoleOutputCP(1251);

gotoxy(sh+28,3);
printf("Курсова робота");

gotoxy(2+sh, 5);
printf("Предметна область: струтури даних і алгоритми");
gotoxy(2+sh, 7);
printf("Тема: задача про рюкзак");

gotoxy(2+sh, 10);
printf("Виконав студент групи КМ-13");
gotoxy(47+sh, 10);
printf("Бобришев О.М.");
gotoxy(33+sh, 16);
printf("2022");

gotoxy(1+sh, 18);
printf("Натисніть будь-яку кнопку для переходу в меню ");
gotoxy(1+sh, 19);
char choice = getch();
if (choice != '\0'){
system("cls");
}
}

int check_stdin(int isLast){ // функція перевірки потоку введення


19

int result = 1;
char ch;

ch = getchar(); // зчитання символу з потоку введення


if(isLast == 0){
if (ch == ' ' || ch == '\n') { // перевірка на те, чи є зчитаний
символ пробілом або переведеня рядка
return result;
}
result = 0; // змінення значення змінною, якщо у потоці будь-які інші
символи
}else{
if (ch == '\n') {
return result;
}
result = 0;
}

return result;
}

int check_value(int a, int isLast){ // перевірка на те, чи були значення


записані в змінні
int isEmpty;
isEmpty = check_stdin(isLast);
if(a == 1 && isEmpty != 0){
return 1;
}else{
return 0;
}

void program(){
struct items inf[1000];
int i = 0;
int len = 0;
char a;
int isNormal_weight;
int isNormal_value;
int max_weight_check;
int m_w;
printf("\nВведіть максимальну вагу, яку може витримати рюкзак: ");

while(1){
fflush(stdin); // очищення потоку введення
max_weight_check = scanf("%d", &m_w);
if(check_value(max_weight_check, 1) == 0){
printf("Для задання максимальною ваги, ви маєте вводити лише цілі
числа\n");
20

printf("Перевведіть інформацію про максимальну вагу: ");


continue;
}
if(m_w < 1){
printf("Для задання ваги мають бути використані числа, більші за
0\n");
printf("Перевведіть інформацію про максимальну вагу: ");
continue;
}
break;
}

printf("\nВведіть построково інформацію про предмети: назву, вагу та


коштовність\n");
printf("При закінченні вводу предеметів у порожньому рядку введіть -\n\
n");

do{
printf("Введіть інформацію про предмет %d: ", i + 1);

while(1){

fflush(stdin); // очищення потоку введення


scanf("%s", &inf[i].name);

isNormal_weight = scanf("%d", &inf[i].weight);

if (check_value(isNormal_weight, 0) == 0){ // перевірка введеного


числа на те, чи є воно цілим
printf("\nДля задання ваги ви маєте вводити лише цілі числа!\
n");
printf("\nПеревведіть інформацію про предмет %d: ", i + 1);
continue;
}
if(inf[i].weight <= 0){ // перевірка введеного числа на те, чи є
воно від'ємним
printf("\nДля введення ваги предмету ви маєте вводити числа,
не менші за нуль\n");
printf("\nПеревведіть інформацію про предмет %d: ", i + 1);
continue;
}

isNormal_value = scanf("%f", &inf[i].value);

if (check_value(isNormal_value, 1) == 0){ // перевірка введеного


числа на те, чи є воно раціональним
printf("\nДля задання коштовності ви маєте вводити раціональні
числа!\n");
printf("\nПеревведіть інформацію про предмет %d: ", i + 1);
continue;
21

}
if(inf[i].value <= 0){ // перевірка введеного числа на те, чи є
воно від'ємним
printf("\nДля введення коштовності ви маєте вводити числа, не
менші за нуль\n");
printf("\nПеревведіть інформацію прол предмет %d: ", i + 1);
continue;
}
break;

}
i++;
len++;
} while (a = getch() != '-' && i <= 100);

printf("\nВведені предмети: \n");


for (int i = 0; i < len; i++){ // виведення введених користувачем
предметів
printf("%d предмет: %s %d %g\n", i+1, inf[i].name, inf[i].weight,
inf[i].value);
}

int *result;
result = (int*) malloc(len * sizeof(int)); // створення динамічного
масиву, кількість елементів якого залежить від кількості введених предметів

float v[len+1][m_w+1]; // створення двовимірного масиву

// заповнення згідно алгоритму динамічного програмування


for(int i = 0; i < len+1; i++){
for(int j = 0; j < m_w + 1; j++){
if((i == 0) || (j == 0)){
v[i][j] = 0;

}else if(j < inf[i-1].weight){


v[i][j] = v[i-1][j];

}else{
v[i][j] = max(v[i-1][j], inf[i-1].value + v[i-1][j-inf[i-
1].weight]);
}
}
}

int k = 0;

for(int i = len; i > 0; i--){


if(v[i][m_w] == v[i-1][m_w]){
continue;
}else{
22

result[k] = i; // заповнення масиву номерами елементів, які мають


бути доданими в рюкзак
k++;
m_w = m_w - inf[i-1].weight;
}
}

if(k == 0){ // перевірка на кількість елементів


printf("\nЖоден із введених предметів не може міститися у рюкзаку");
}else{
printf("\nПотрібно взяти такі предмети: \n");
for(int i = k - 1; i >= 0; i--){ // виведення результату
printf("%d премдет: %s %d %g\n", result[i], inf[result[i]-1].name,
inf[result[i]-1].weight,inf[result[i]-1].value);
}
}
}

int main(void){

intro();

while(1){
int number;
printf("\nОберіть один із варіантів:\n");
printf("1 - задача про рюкзак\n");
printf("2 - повернутися до початкового екрану\n");
printf("Будь-яка інша кнопка - вихід із програми\n");
printf("Уведіть номер обраного пункту: ");

scanf("%d", &number);
if (number == 1){
program();

int clear;
printf("\n\nОберіть один із варіантів:\n");
printf("1 - очистити коносоль вводу\n");
printf("будь-який символ - продовжити\n");
printf("Уведіть номер обраного пункту: ");
scanf("%d", &clear);
if (clear == 1){
system("cls");
continue;
}
}else if(number == 2){
system("cls");
intro();
}else{
printf("Програму завершено!");
break;
23

}
}
}

You might also like