10.3. Демонстрация. Решаем проблему о рюкзаке кодом
Шаг 1
Для начала создадим нашу таблицу, словно мы решаем задачу «руками» — проходимся по алгоритму и исполняем его «в голове». Будем выводить её результат в конце функции, чтобы убедиться, что мы получаем то же самое, что насчитали «руками».
import {logResult, logTable} from "./logger";
// Напишем нашу функцию для оптимизации, принимающую массив размеров частей и размер пачки
function prioritize(filePartSizes, chunkSize) {
// создаем нашу табличку
const table = Array(filePartSizes.length).fill( Array(chunkSize).fill(0) );
logTable(table);
// Результат — нижняя правая ячейка
return table[filePartSizes.length - 1][chunkSize - 1];
}
logResult(prioritize([4, 5, 7], 10));
Шаг 2
Заполним таблицу без обновлений максимума с помощью предыдущей подзадачи. На этом примере хорошо видно, как без переиспользования решение нашей задачи стало обычным жадным алгоритмом, только решённым для частей разных размеров.
import {logResult, logTable} from "./logger";
// Напишем нашу функцию для оптимизации, принимающую массив размеров частей и размер пачки
function prioritize(filePartSizes, chunkSize) {
// создаем нашу табличку
const table = Array(filePartSizes.length).fill().map(() => Array(chunkSize).fill(0));
// Заполняем каждую строчку последовательно
for (let rowIndex = 0; rowIndex < filePartSizes.length; rowIndex++) {
for (let cellIndex = 0; cellIndex < chunkSize; cellIndex++) {
const currentChunkSize = cellIndex + 1;
const currentFilePartSize = filePartSizes[rowIndex];
// считаем максимальное количество места, которое мы можем занять частями текущего размера
const maximumCurrent = Math.floor(currentChunkSize / currentFilePartSize) * currentFilePartSize;
// максимальное количество данных, которое мы можем положить в оставшееся количество места
// берем максимум решения предыдущей подзадачи для оставшегося места если оно есть, либо 0
const maximumRest = table[rowIndex - 1] && table[rowIndex - 1][cellIndex - maximumCurrent] || 0;
// получаем общее решение для данной ячейки
const solution = maximumCurrent + maximumRest;
table[rowIndex][cellIndex] = solution;
}
}
logTable(table);
// Результат — нижняя правая ячейка
return table[filePartSizes.length - 1][chunkSize - 1];
}
logResult(prioritize([4, 5, 7], 10));
Шаг 3
Добавим операцию максимума над текущим и предыдущим решением и получим таблицу, идентичную той, что мы заполняли «руками»!
import {logResult, logTable} from "./logger";
// Напишем нашу функцию для оптимизации, принимающую массив размеров частей и размер пачки
function prioritize(filePartSizes, chunkSize) {
// создаем нашу табличку
const table = Array(filePartSizes.length).fill().map(() => Array(chunkSize).fill(0));
// Заполняем каждую строчку последовательно
for (let rowIndex = 0; rowIndex < filePartSizes.length; rowIndex++) {
for (let cellIndex = 0; cellIndex < chunkSize; cellIndex++) {
const currentChunkSize = cellIndex + 1;
const currentFilePartSize = filePartSizes[rowIndex];
// считаем максимальное количество места, которое мы можем занять частями текущего размера
const maximumCurrent = Math.floor(currentChunkSize / currentFilePartSize) * currentFilePartSize;
// максимальное количество данных, которое мы можем положить в оставшееся количество места
// берем максимум решения предыдущей подзадачи для оставшегося места если оно есть, либо 0
const maximumRest = table[rowIndex - 1] && table[rowIndex - 1][cellIndex - maximumCurrent] || 0;
// получаем общее решение для данной ячейки
const solution = maximumCurrent + maximumRest;
// Если есть, с чем сравнить текущее решение — сравниваем и берем максимум
table[rowIndex][cellIndex] = rowIndex > 0
? Math.max(solution, table[rowIndex - 1][cellIndex])
: solution;
}
}
logTable(table);
// Результат — нижняя правая ячейка
return table[filePartSizes.length - 1][chunkSize - 1];
}
logResult(prioritize([4, 5, 7], 10));
Шаг 4
Итак, мы реализовали нашу функцию. В прошлой статье мы заверили вас, что ни от изменения частей местами, ни от добавления новых алгоритмов решение не сломается. Давайте посмотрим на решение этой задачи с другими аргументами. Всё ещё работает!
import {logResult, logTable} from "./logger";
// Напишем нашу функцию для оптимизации, принимающую массив размеров частей и размер пачки
function prioritize(filePartSizes, chunkSize) {
// создаем нашу табличку
const table = Array(filePartSizes.length).fill().map(() => Array(chunkSize).fill(0));
// Заполняем каждую строчку последовательно
for (let rowIndex = 0; rowIndex < filePartSizes.length; rowIndex++) {
for (let cellIndex = 0; cellIndex < chunkSize; cellIndex++) {
const currentChunkSize = cellIndex + 1;
const currentFilePartSize = filePartSizes[rowIndex];
// считаем максимальное количество места, которое мы можем занять частями текущего размера
const maximumCurrent = Math.floor(currentChunkSize / currentFilePartSize) * currentFilePartSize;
// максимальное количество данных, которое мы можем положить в оставшееся количество места
// берем максимум решения предыдущей подзадачи для оставшегося места если оно есть, либо 0
const maximumRest = table[rowIndex - 1] && table[rowIndex - 1][cellIndex - maximumCurrent] || 0;
// получаем общее решение для данной ячейки
const solution = maximumCurrent + maximumRest;
// Если есть, с чем сравнить текущее решение — сравниваем и берем максимум
table[rowIndex][cellIndex] = rowIndex > 0
? Math.max(solution, table[rowIndex - 1][cellIndex])
: solution;
}
}
logTable(table);
// Результат — нижняя правая ячейка
return table[filePartSizes.length - 1][chunkSize - 1];
}
logResult(prioritize([8, 5, 4, 7], 11));
File logger.js
const table = document.getElementById('table');
const result = document.getElementById('result');
export function logTable(data) {
const body = document.createElement('tbody');
for (const line of data) {
const lineElement = document.createElement('tr');
for (const cell of line) {
const cellElement = document.createElement('td');
cellElement.innerText = cell;
lineElement.appendChild(cellElement);
}
body.appendChild(lineElement);
}
table.appendChild(body);
}
export function logResult(res) {
result.innerText = `Максимальное количество занимаемого места — ${res} МБ`;
}