жадный множественный рюкзак (минимизировать/уменьшить количество корзин)

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

как я столкнулся с этой проблемой (не относится к самой проблеме, но может быть интересно):

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

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

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

У меня было не так много времени, поэтому я написал небольшую жадную функцию, которая группирует наборы вместе.

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

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

def group_to_similar_sizes(orig, max_size=None, max_factor=None):
    """group orig list in sections that to not overflow max(orig) (or given max_size).

    return list of grouped indices, plus max effective length.

    >>> group_to_similar_sizes([1, 3, 7, 13])
    ([[2, 1, 0], [3]], 13)
    >>> group_to_similar_sizes([2, 9, 9, 11, 12, 19, 19, 22, 22, ])
    ([[3, 1], [4, 2], [5], [6, 0], [7], [8]], 22)

    result is independent of original ordering
    >>> group_to_similar_sizes([9, 19, 22, 12, 19, 9, 2, 22, 11, ])
    ([[3, 1], [4, 2], [5], [6, 0], [7], [8]], 22)

    you can specify a desired max size
    >>> group_to_similar_sizes([2, 9, 9, 11, 12, 19, 19, 22, 22, ], 50)
    ([[3, 2, 1], [6, 5, 4], [8, 7, 0]], 50)

    if the desired max size is too small, it still influences the way we make groups.
    >>> group_to_similar_sizes([1, 3, 7, 13], 8)
    ([[1], [2, 0], [3]], 13)
    >>> group_to_similar_sizes([2, 9, 9, 11, 12, 19, 19, 22, 22, ], 20)
    ([[1], [3, 2], [4, 0], [5], [6], [7], [8]], 22)

    max size can be adjusted by a multiplication factor
    >>> group_to_similar_sizes([9, 19, 22, 12, 5, 9, 2, 22, 11, ], max_factor=0.75)
    ([[2], [3], [4, 1], [5, 0], [6], [7], [8]], 22)
    >>> group_to_similar_sizes([9, 19, 22, 12, 5, 9, 2, 22, 11, ], max_factor=1.5)
    ([[2, 1], [6, 5], [7, 3, 0], [8, 4]], 33)
    """

    ordered = sorted(orig)
    max_size = max_size or ordered[-1]
    if max_factor is not None:
        max_size = int(max_size * max_factor)

    orig_ordered = list(ordered)
    todo = set(range(len(orig)))
    effective_max = 0

    result = []
    ## while we still have unassigned items
    while ordered:
        ## choose the largest item
        ## make it member of a group
        ## check which we can still put in its bin

        candidate_i = len(ordered) - 1
        candidate = ordered.pop()
        if candidate_i not in todo:
            continue
        todo.remove(candidate_i)

        group = [candidate_i]
        group_size = candidate

        for j in sorted(todo, reverse=True):
            if orig_ordered[j] + group_size <= max_size:
                group.append(j)
                group_size += orig_ordered[j]
                todo.remove(j)

        result.insert(0, group)
        effective_max = max(group_size, effective_max)

    return result, effective_max

person mariotomo    schedule 18.05.2011    source источник


Ответы (1)


Мне нравится идея вашего коллеги добавить некоторые шумовые данные, но, может быть, лучше сделать несколько свопов в ordered после вызова ordered = sorted(orig)?

person Nikiton    schedule 18.05.2011
comment
ах, да, это также была моя интерпретация добавления шума к данным. - person mariotomo; 18.05.2011
comment
@mariotomo Хорошо, я этого не уловил. И еще одна мысль в том же направлении: как насчет определения минимального количества ячеек для небольшого числа (2,3,4...)? Я тоже думаю, что это какой-то шум...(?) - person Nikiton; 18.05.2011