Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Интересная задачка :) (https://javascript.ru/forum/misc/69924-interesnaya-zadachka.html)

-FIXER- 27.07.2017 17:50

Интересная задачка :)
 
Здравствуйте. Столкнулся с проблемой и котелок не может придумать решение. Хочу подключить коллективный мозг. Для упрощения понимания придумаю гипотетический пример. Суть следующая:

Предположим, у нас есть некий магазин в котором нужно купить определённый товар, который стоит (X) долларов. Х - это переменная и каждый раз её значение меняется. Ну для примера возьмём за X сумму в $117.47. Но самих денег у нас нет, а оплачиваем мы акциями(или, если хотите, марками, или фантиками). Суть в том, что акции/марки/фантики стоят тоже по-разному. Допустим, акции у нас разных фирм и каждая акция стоит по-своему. Цена может колебаться от $0.5 до $200 за штуку. И задача состоит в том, чтобы набрать акций на сумму ровно $117.47, если это возможно, а если невозможно, то на максимально близкую сумму к нашему (X), но не меньше. Т.е. можно набрать акций на $117.47, на $117.48, $117.49, $117.50. Если разница больше, чем в 4 цента, то это уже критично.

Для примера вот массив с ценами наших акций: [0.67, 0.84, 0.86, 0.86, 0.89, 1.7, 1.8, 1.87, 1.99, 2, 2.01, 2.06, 2.09, 2.85, 2.88, 2.99, 3.14, 3.17, 4.45, 4.49, 5.01, 5.76, 5.78, 8.91, 9, 12.54, 17.83, 54.11, 77.12, 103.3, 140.88]
У меня в голову только дуратские варианты лезут. Может у кого-нибудь есть более-менее деликатные варианты. Важно чтобы это было не очень ресурсозатратно и времязатратно, в случае если массив цен акций содержит большое количество элементов. Обычно это не больше 300 элементов.

j0hnik 27.07.2017 18:03

1п. берем последнюю ячейку массива, если она больше стоимости меняем ее на предпоследнюю и так далее.. пока не дойдем до меньшей или до наиболее минимально возможной.
2п. если последняя ячейка не больше стоимоти, то сохраняем ее в переменную и к ней будем плюсовать следующую по тому же алгоритму что и первом пункте.

j0hnik 27.07.2017 18:06

чтобы максимально точно попасть в стоимость нужно будет использовать какой то более сложный алгоритм. это уже вопрос больше к математикам.

-FIXER- 27.07.2017 18:16

Цитата:

Сообщение от j0hnik (Сообщение 459933)
1п. берем последнюю ячейку массива, если она больше стоимости меняем ее на предпоследнюю и так далее.. пока не дойдем до меньшей или до наиболее минимально возможной.
2п. если последняя ячейка не больше стоимоти, то сохраняем ее в переменную и к ней будем плюсовать следующую по тому же алгоритму что и первом пункте.

Спасибо за ответ, но Ваш вариант не подходит. На самом деле у меня в голове этот вариант изначально возник, но он не позволит максимально приблизиться к необходимой сумме. Разница может достигать высоких значений (пол доллара или доллар), а максимально допустимая разница - 4 цента.

laimas 27.07.2017 18:18

Цитата:

Сообщение от -FIXER-
Т.е. можно набрать акций на $117.47, на $117.48, $117.49, $117.50. Если разница больше, чем в 4 цента, то это уже критично.

Тогда почему разрешен выбор 140.88? Или для указанного диапазона цен акций есть некий критерий на который можно набрать, и если да, то как он определяется?

рони 27.07.2017 18:40

-FIXER-,
так?
alert(2*54.11 +0.84 * 10 + 1 * 0.86)

j0hnik 27.07.2017 18:51

Цитата:

Сообщение от рони (Сообщение 459939)
-FIXER-,
так?
alert(2*54.11 +0.84 * 10 + 1 * 0.86)

Так, только алгоритм нужен, для любых чисел я так понял

рони 27.07.2017 20:55

:write: :)
alert(2.06 * 49 + 5.76 * 2 + 5.01)//117.47

j0hnik 27.07.2017 21:27

Цитата:

Сообщение от рони (Сообщение 459947)
:write: :)
alert(2.06 * 49 + 5.76 * 2 + 5.01)//117.47

вручную подбирал? :write:

рони 27.07.2017 21:45

Цитата:

Сообщение от j0hnik
вручную подбирал?

да

nerv_ 27.07.2017 22:06

https://ru.wikipedia.org/wiki/Задача_о_ранце

Alexandroppolus 28.07.2017 09:14

Цитата:

Сообщение от nerv_ (Сообщение 459956)

+1

причем, тут "ранцем" будет разница между ценой предмета и суммарной стоимостью всех имеющихся акций, а "весом" и "ценностью" каждой акции - её стоимость. Тогда решение - набор тех акций, которые не используем при покупке.

если каждой акции по одной штуке, то это "Рюкзак 0-1", и можно сказать, повезло :)

рони 28.07.2017 10:38

nerv_,
Alexandroppolus,
ничего не понимаю, ни википедию, ни вас, а код существует для такого?
P.S. пробовал написать полный перебор, комп не справляется, видимо не слишком оптимально или где ошибка.

Alexandroppolus 28.07.2017 12:13

Цитата:

Сообщение от рони
пробовал написать полный перебор, комп не справляется, видимо не слишком оптимально или где ошибка.

полный перебор дает сложность O(2^N), и если N порядка 200-300, то смысла писать код вообще нет - всё равно будет выполняться целую вечность.

"Рюкзак 0-1" разруливается за полиномиальное время (на подобных объемах отработает мгновенно).
Код надо поискать, должен быть )

рони 28.07.2017 12:21

Alexandroppolus,
спасибо, ещё бы понять что такое
Цитата:

Сообщение от Alexandroppolus
"Рюкзак 0-1"

:)

nerv_ 28.07.2017 12:56

Цитата:

Сообщение от рони
ещё бы понять что такое

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.B 0.D0.BA.D0.B5_0-1

https://ru.wikipedia.org/wiki/Задача_о_ранце#.D0.92.D0.B0.D1.80.D0.B 8.D0.B0.D0.BD.D1.82.D1.8B_.D0.B7.D0.B0.D0.B4.D0.B0 .D1.87.D0.B8_.D0.BE_.D1.80.D0.B0.D0.BD.D1.86.D0.B5

рони 28.07.2017 15:15

nerv_,
спасибо, но я не понимаю что там написано, особенно математические формулы.
Цитата:

Решение задачи о рюкзаке 0-1 близко к решению предыдущей задачи, но необходимо учесть тот факт, что каждый предмет имеется в единственном экземпляре.
например тут написано в единственнном почему??? у меня получилось 2.06 * 49 + 5.76 * 2 + 5.01 -- 49 экзепляров одного предмета, другого 2, третьего один.

nerv_ 29.07.2017 10:18

рони,

Цитата:

Сообщение от рони
например тут написано в единственнном почему???

потому, что существует ряд формулировок (вариантов) задач о рюкзаке. Формулировка, готорая гласит
Цитата:

не более одного экземпляра каждого предмета
носит название Рюкзак 0-1

Что я сделал:
1. взял N-первых весов из первого поста
2. домножил их на 100, чтобы получить целые числа
3. задал цель равную 247
4. взял алгоритм из википедии для рюкзака 0-1

результат см. ниже

const costs = [67, 84, 86, 86, 89, 170, 180, 187, 199, 200, 201, 206, 209]
const capacity = 247

/**
 * @see [url]https://ru.wikipedia.org/wiki/Задача_о_ранце[/url]
 * @param {Array.<Number>} v Ценности предметов (загруженные в массив v)
 * @param {Array.<Number>} w Веса предметов (загруженные в массив w)
 * @param {Number} n Количество предметов (n)
 * @param {Number} W Грузоподъемность (W)
 * @return {Array.<Number>}
 */
function solveKnapsack01(v, w, n, W) {
  let m = new Array(n)

  for (let i = 0; i <= n; i++) {
    m[i] = new Array(W + 1).fill(0)
  }

  for (let i = 1; i < n; i++) {
    for (let j = 0; j <= W; j++) {
      if (w[i] > j) {
        m[i][j] = m[i - 1][j]
      } else {
        m[i][j] = Math.max(m[i - 1][j], m[i - 1][j - w[i]] + v[i])
      }
    }
  }

  return m
}


console.log(solveKnapsack01(costs, costs, costs.length, capacity))


Но это еще не все решение:
Цитата:

Таким образом, любому решению задачи соответствует некоторый путь в построенном дереве. Задача сводится к нахождению пути максимальной длины[
Т.е. нужно в получившемся массиве (дереве) найти путь максимальной длинны.

---

Также обращаю внимание на пост Alexandroppolus:
Цитата:

Сообщение от Alexandroppolus
причем, тут "ранцем" будет разница между ценой предмета и суммарной стоимостью всех имеющихся акций, а "весом" и "ценностью" каждой акции - её стоимость. Тогда решение - набор тех акций, которые не используем при покупке.

Понятно почему? :)

рони 29.07.2017 10:57

nerv_,
спасибо за попытку, но бесполезно - не понимаю как строится дерево, не понимаю что за результат в строке 33, и уж тем более почему надо искать набор акций которые не используем при покупке.
то что такой вариант называется Рюкзак 0-1, пусть, но мне это ни очём не говорит.

как там говорится: нельзя обьять необьятное, но зато мы поём и пляшем.

рони 29.07.2017 11:15

Цитата:

Сообщение от nerv_
задал цель равную 247

:write: :) [67,180] вот что ожидал увидеть в строке 33 пост №18

-FIXER- 05.08.2017 13:04

:no: Конечно алгоритм нужен


Часовой пояс GMT +3, время: 23:59.