Нахождение оптимальной комбинации размера файла - PullRequest
8 голосов
/ 02 сентября 2010

Это проблема, я думаю, что для нее уже есть алгоритм - но я не знаю правильных слов для использования с Google, кажется:).

Проблема: я хотел бы сделать небольшую программу, с помощью которой я бы выбрал каталог, содержащий любые файлы (но для моих целей медиа-файлы, аудио и видео). После этого я хотел бы ввести в МБ максимальный общий размер файла, который не должен превышаться. В этот момент вы нажмете кнопку «Рассчитать наилучшее соответствие».

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

Таким образом, вы можете узнать, какие файлы объединять при записи CD или DVD, чтобы вы могли использовать как можно больше диска.

Я пытался сам придумать алгоритм для этого - но не смог: (.

Кто-нибудь знает какой-нибудь хороший алгоритм для этого?

Заранее спасибо:)

Ответы [ 6 ]

5 голосов
/ 02 сентября 2010

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

Такие проблемы обычно очень трудно решить.Но если вас интересуют почти оптимальные решения, вы можете использовать недетерминированные алгоритмы, такие как имитация отжига .Скорее всего, вы не получите оптимальное решение, но почти оптимальное.

Эта ссылка объясняет, как симуляция отжига может решить проблему ранца и поэтому должна быть вам интересна.1015 *

4 голосов
/ 02 сентября 2010

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

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

Код основан на http://en.wikipedia.org/wiki/Knapsack_problem#0-1_knapsack_problem,, следовательно, наименее информативные имена переменных m, W, w, v.

#!/usr/bin/python

import sys

solcount = 0

class Solution(object):
    def __init__(self, items):
        object.__init__(self)
        #self.items = items
        self.value = sum(items)
        global solcount
        solcount += 1
    def __str__(self):
        #return str(self.items) + ' = ' + str(self.value)
        return ' = ' + str(self.value)

m = {}

def compute(v, w):
    coord = (len(v),w)
    if coord in m:
        return m[coord]
    if len(v) == 0 or w == 0:
        m[coord] = Solution([])
        return m[coord]
    newvalue = v[0]
    newarray = v[1:]
    notused = compute(newarray, w)
    if newvalue > w:
        m[coord] = notused
        return notused
    # used = Solution(compute(newarray, w - newvalue).items + [newvalue])
    used = Solution([compute(newarray, w - newvalue).value] + [newvalue])
    best = notused if notused.value >= used.value else used
    m[coord] = best
    return best

def main():
    v = [int(l) for l in open('filesizes.txt')]
    W = int(sys.argv[1])
    print len(v), "items, limit is", W
    print compute(v, W)
    print solcount, "solutions computed"

if __name__ == '__main__':
    main()

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

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

Со списком из 100 случайно сгенерированных размеров файлов в диапазоне 2000-6000(Я предполагаю, что блоки 2 КБ, то есть файлы размером 4-12 МБ), это решает проблему W = 40 КБ за 100 секунд на моем ноутбуке.При этом он вычисляет 2,6 млн. Возможных решений на 4 млн.

Сложность равна O (W * n), где n - количество файлов.Это не противоречит тому факту, что задача является NP-полной.Поэтому я, по крайней мере, подхожу к решению, и это только в неоптимизированном Python.

Очевидно, что теперь требуется некоторая оптимизация, потому что на самом деле ее нужно решить для W = 4M (DVD на 8 ГБ) и скольких файлов выесть (скажем, несколько тысяч).Предполагая, что программе разрешено занимать 15 минут (сравнимо с временем, необходимым для записи DVD), это означает, что производительность в настоящее время невелика примерно в 10 ^ 3 раз.Таким образом, у нас есть проблема, которую довольно сложно быстро и точно решить на ПК, но не за пределами технологии.

Использование памяти является основной проблемой, так как, как только мы начнем использовать swap, мы замедлим,и если у нас заканчивается виртуальное адресное пространство, у нас большие проблемы, потому что мы должны реализовать собственное хранилище решений на диске.Мой тестовый запуск достигает пика в 600 МБ.Если вы написали код на C на 32-битной машине, каждое «решение» имеет фиксированный размер 8 байтов.Таким образом, вы можете сгенерировать массивный двумерный массив из них без выделения памяти в цикле, но в 2 ГБ ОЗУ вы можете обрабатывать только W = 4M и n = 67.К сожалению, DVD вышли.Это может почти решить проблему для компакт-дисков размером 2 k, хотя: W = 350k дает n = 766.

Редактировать: предложение MAK вычислять итеративно снизу вверх, а не рекурсивно сверху вниз, должно значительно уменьшитьтребование к памяти.Сначала вычислите m (1, w) для всех 0 <= w <= W. Из этого массива вы можете вычислить m (2, w) для всех 0 <= w <= W. Затем вы можете выбросить все m (1, w) значения: они вам не понадобятся для вычисления m (3, w) и т. Д. </p>

Кстати, я подозреваю, что на самом деле проблема, которую вы хотите решить, может быть упаковка бинапроблема , а не просто вопрос о том, как добиться максимально возможного заполнения DVD.Вот если у вас есть куча файлов, вы хотите записать их все на DVD, используя как можно меньше DVD.Существуют ситуации, когда решить проблему упаковки бункера очень просто, но решить эту проблему сложно.Например, предположим, что у вас есть 8 ГБ дисков и 15 ГБ небольших файлов.Потребуется некоторый поиск, чтобы найти максимально возможное совпадение с 8 ГБ, но проблема упаковки бина будет решена тривиально, если поместить примерно половину файлов на каждый диск - не имеет значения, как именно вы их разделите, потому что выбудешь тратить 1 ГБ места, что бы ты ни делал.

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

2 голосов
/ 02 сентября 2010

Похоже, у вас есть хард проблема там.Эта проблема хорошо известна, но эффективных решений (может?) Не существует.

0 голосов
/ 02 сентября 2010

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

Предположим, B - это размер всех файлов, а C - емкость каждого диска. Тогда вам понадобится как минимум n = roundup (B / C) дисков. Попробуйте разместить все файлы на n дисках. Если вы можете сделать это, вы закончили и нашли оптимальное решение. В противном случае попробуйте разместить все файлы на n + 1 дисках. Если вы можете сделать это, у вас есть эвристическое решение; в противном случае попробуйте разместить файлы на n + 2 дисках и т. д., пока вы не сможете это сделать.

Для любого данного размещения файлов на дисках ниже (которое может превышать некоторые емкости диска), пусть si будет объединенным размером файлов, выделенных для диска i, и t = max si. Мы закончили, когда t <= C. </p>

Сначала упорядочите (и индексируйте) файлы от наименьшего к размеру.

Для m> = n дисков,

  1. Распределите файлы на диски в обратном порядке: 1-> 1, 2-> 2, ... m-> m, m + 1> m-1, m + 2 -> m-2, ... 2m-> 1, 2m + 1-> 2, 2m + 2-> 3 ... 3m-> m, 3m + 1-> m-1 и так далее, пока все файлы распределяются, без учета емкости диска. Если t <= C, то мы закончили (и распределение оптимально, если m = n); иначе перейдите к # 2. </p>

  2. Попытка уменьшить t путем перемещения файла с диска i с si = t на другой диск без увеличения t. Продолжайте делать это до тех пор, пока t <= C, и в этом случае мы закончим (и распределение будет оптимальным, если m = n), или t не может быть уменьшено в дальнейшем, и в этом случае перейдем к # 3. </p>

  3. Попытайтесь уменьшить t, выполнив попарные обмены между дисками. Продолжайте делать это до тех пор, пока t <= C, и в этом случае мы закончим (и распределение будет оптимальным, если m = n), или t не может быть уменьшено в дальнейшем с помощью парных обменов. В последнем случае повторите # 2, если не было сделано никаких улучшений в последний раз, когда # 2 был повторен, и в этом случае увеличьте m на единицу и повторите # 1. </p>

В # 2 и # 3, конечно, существуют разные способы упорядочения возможных перераспределений и попарных обменов.

0 голосов
/ 02 сентября 2010

Спасибо за ваши ответы.

Я больше разбирался в этой проблеме, руководствуясь приведенными ответами. Среди прочего, я нашел эту веб-страницу, http://www.mathmaniacs.org/lessons/C-subsetsum/index.html. Она рассказывает о проблеме подмножества сумм, которая, я считаю, является проблемой, которую я описал здесь.

Вот одно предложение с веб-страницы:

-

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

-

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

CD с mp3: s может легко иметь 100 mp3 и DVD намного больше, что приводит к тому, что солнце сгорает, прежде чем я получу ответ:).

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

Так что, я думаю, я просто продолжаю оценивать хорошую комбинацию файлов для записи на CD и DVD. :)

Спасибо за помощь. :)

0 голосов
/ 02 сентября 2010

Кроме очевидного способа пробовать все комбинации объектов с размером bucketizer perl, который выполняет именно то, что вы запрашиваете.Я не уверен, что именно он делает, но в руководстве упоминается, что существует один способ "грубой силы", поэтому я предполагаю, что должна быть какая-то оптимизация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...