Алгоритм возможных сумм (превышающих), уплаченных за определенную цену, основанный на наименованиях

В текущем проекте люди могут заказывать товары, доставленные к их двери, и выбирать "оплату при доставке" в качестве варианта оплаты. Чтобы убедиться, что у парцевания доставки достаточно изменений, клиентам предлагается ввести сумму, которую они будут платить (например, доставка 48,13, они будут платить 60, - (3 * 20, -)). Теперь, если бы это зависело от меня, я бы сделал это свободным полем, но решительные вышестоящие решения решили, что это должен быть выбор, основанный на доступных номиналах, без предоставления сумм, которые приведут к набору наименований, которые могут быть меньше.

Пример:

denominations = [1,2,5,10,20,50]
price = 78.12
possibilities:
    79  (multitude of options),
    80  (e.g. 4*20)
    90  (e.g. 50+2*20)
    100 (2*50)

Это международный, поэтому наименования могут меняться, и алгоритм должен основываться на этом списке.

Ближайший я пришел, который, кажется, работает, это:

for all denominations in reversed order (large=>small)
    add ceil(price/denomination) * denomination to possibles
    baseprice = floor(price/denomination) * denomination;
    for all smaller denominations as subdenomination in reversed order
        add baseprice + (ceil((price - baseprice) / subdenomination) * subdenomination) to possibles
    end for
end for
remove doubles
sort

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

Поскольку это, вероятно, не новая проблема, и Google et al. не мог предоставить мне ответ, за исключением загрузок страниц, подсчитывающих, как сделать точные изменения, я думал, что спрошу: вы решили эту проблему раньше? Какой алгоритм? Любое доказательство это всегда будет работать?

Ответ 1

Его применение Жадного алгоритма http://mathworld.wolfram.com/GreedyAlgorithm.html (Алгоритм, используемый для рекурсивного построения набора объектов из наименьших возможных составляющих частей )

Псевдокод

list={1,2,5,10,20,50,100} (*ordered *)
while list not null
   found_answer = false
   p = ceil(price) (* assume integer denominations *)
   while not found_answer
      find_greedy (p, list) (*algorithm in the reference above*)
      p++
   remove(first(list))

EDIT > некоторые итерации нонсенs >

list={1,2,5,10,20,50,100} (*ordered *)
p = ceil(price) (* assume integer denominations *)
while list not null
   found_answer = false
   while not found_answer
      find_greedy (p, list) (*algorithm in the reference above*)
      p++
   remove(first(list))

ИЗМЕНИТЬ >

Я нашел улучшение из-за Пирсона по алгоритму Жадный. Его O (N ^ 3 log Z), где N - число наименований, а Z - наибольшая веточка множества.

Вы можете найти его в http://library.wolfram.com/infocenter/MathSource/5187/

Ответ 2

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

Имея эту базу данных, вы можете просто получить все возможное, переплачивая одним запросом,

WHERE sum >= cost and sum <= cost + epsilon

Некоторое слово об эпсилоне, хм.. вы можете присвоить его из стоимости? Возможно, 10% стоимости + 10 баксов?:

WHERE sum >= cost and sum <= cost * 1.10 + 10

Структура таблицы должна иметь количество столбцов, представляющих количество монет и тип бумаги. Значение каждого столбца имеет количество вхождений этого типа платного элемента.

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


Другой способ: от cost до cost + epsilon и для каждого значения рассчитывать наименьшее возможное количество платных предметов для каждого. У меня есть алгоритм для этого. Вы можете сделать это с помощью этого алгоритма, но это в С++:

int R[10000];
sort(C, C + coins, cmp);

R[0]=0;

for(int i=1; i <= coins_weight; i++)
{
    R[i] = 1000000;
    for (int j=0; j < coins; j++) 
    {
        if((C[j].weight <= i) && ((C[j].value + R[i - C[j].weight]) < R[i]))
        {
            R[i] = C[j].value + R[i - C[j].weight];
        }
    }
}

return R[coins_weight];

Забастовкa >