2010-09-02 8 views
7

Esto es un problema, creo que ya hay un algoritmo, pero no sé las palabras correctas para usar con google, parece :).Encontrar la combinación óptima de tamaño de archivo

El problema: me gustaría hacer un pequeño programa con el que seleccionaría un directorio que contiene cualquier archivo (pero para mi propósito archivos multimedia, audio y video). Después de eso, me gustaría ingresar en MB la suma máxima del tamaño total del archivo que no debe excederse. En este punto, presionarás el botón "Calcular el mejor ajuste".

Este botón debe comparar todos los archivos en el directorio y proporcionar como resultado una lista de los archivos que cuando se juntan se acerca más al tamaño máximo de archivo sin sobrepasar el límite.

De esta manera podría averiguar qué archivos combinar al grabar un CD o DVD para poder utilizar tanto como sea posible el disco.

he tratado de llegar con el algoritmo para esto mismo - pero fallaron :(

Alguien sabe de algún buen algoritmo para hacer esto

Gracias de antemano :)

+3

Me recuerda a http://xkcd.com/287/ – ruslik

+0

Supongo que la definición correcta del problema sería "Cómo dividir estos archivos en el número mínimo de contenedores, para que los archivos de cada contenedor no excedan el límite de tamaño. ", y es un problema más difícil que la mochila. – ruslik

Respuesta

2

.? Parece que tienes un problema hard allí. Este problema es bien conocido, pero no existen soluciones eficientes (¿puede?).

+0

soluciones eficientes * aproximadas * existen. –

+2

Existen soluciones exactas razonablemente eficientes. La solución de programación dinámica es pseudopolinomial. Y, afortunadamente, el tamaño de un DVD es constante, al menos hasta que obtenga una unidad Blu-Ray-W. Así que, al menos, le doy una oportunidad a la solución DP. Puede fallar para directorios grandes, cierto, pero realmente no sé si "grande" es más o menos que, digamos, 10000 archivos. Consejo: redondee primero todos los tamaños de archivo al tamaño del bloque del sistema de archivos DVD: acelerará considerablemente la solución DP y dará * más * resultados precisos. –

+0

(1 hora más tarde) Ahora sé que "grande" es menos de 10000 archivos, después de haber dado una solución DP. Pero no es un problema escandalosamente difícil, es en esa molesta zona de "un orden de magnitud a menos que yo pueda pensar en algo inteligente". –

0

Aparte de la forma obvia de probar todas las permuations de objetos con tamaño < cubo, también puede echar un vistazo a la implementación del módulo perl bucketizer, que hace exactamente lo que está pidiendo. No estoy seguro de lo que hace exactamente, pero el manual menciona que hay una forma de "fuerza bruta", así que supongo que también debe haber algún tipo de optimización.

0

Gracias por sus respuestas.

He examinado este problema más ahora con la orientación de las respuestas dadas. Entre otras cosas, encontré esta página web, http://www.mathmaniacs.org/lessons/C-subsetsum/index.html. Habla sobre el problema de suma de subconjuntos, que creo que es el problema que describí aquí.

Una frase de la página web es la siguiente:

-

Es posible que desee señalar que un número como 2300 es tan grande que incluso un conteo ordenador a una velocidad de más de un millón o mil millones cada segundo, no llegaría a 2300 hasta mucho después de que nuestro sol se haya apagado.

-

Personalmente me gustaría tener más uso de este algoritmo cuando se compara una cantidad más grande de tamaño de los archivos que digamos 10 o menos, ya que de alguna manera es fácil llegar a la probablemente mayor suma simplemente por ensayo y error manualmente si la cantidad de archivos es baja

Un CD con mp3: s puede tener fácilmente 100 mp3 y un DVD mucho más, lo que lleva al sol a quemarse antes de que tenga la respuesta :).

Intentar encontrar al azar la suma óptima al parecer puede acercarlo bastante, pero nunca se puede garantizar que sea la respuesta óptima y, con mala suerte, también puede estar muy lejos. La fuerza bruta es la única forma real en que parece obtener la respuesta óptima y eso llevaría demasiado tiempo.

Así que supongo que simplemente sigo estimando manualmente una buena combinación de archivos para grabar en CD y DVD. :)

Gracias por la ayuda. :)

+1

no, estás siendo excesivamente pesimista. Acabo de descifrar un código en Python, completamente sin optimizar. Suponiendo un tamaño de bloque de 2kb, resolverá la versión de decisión del problema para 100 archivos (tamaños aleatorios entre 2k-6k bloques) y un disco de tamaño 40000 bloques (es decir, 80MB), en 100 segundos. Obviamente, ese no es tu problema, pero al menos está en el estadio, y el sol aún no se ha apagado, lo he notado ;-). A pesar de lo que ha leído, la solución exacta es en realidad O (n * m), donde n es la cantidad de archivos ym es el tamaño del DVD. Es * no * exponencial. –

5

Esto es, como se señaló otro, el problema de la mochila, que es un problema de optimización combinatoria. Significa que busca algún subconjunto o permutación de un conjunto que minimice (o maximice) un cierto costo. Otro problema bien conocido es el Traveling Salesman Problem.

Estos problemas suelen ser muy difíciles de resolver. Pero si está interesado en soluciones casi óptimas, puede usar algoritmos no deterministas, como simulated annealing. Lo más probable es que no obtenga la solución óptima, sino una casi óptima.

This link explica cómo el recocido simulado puede resolver el problema de la mochila, y por lo tanto debería ser interesante para usted.

+0

Todo verdadero, guarde una cosa: ¿podría? obtenga una solución óptima, simplemente no hay * garantía * de que lo hará, depende del tamaño del espacio de búsqueda y de la suerte que tenga, ciertamente poco probable, pero también lo es la lotería, y la gente sí gana ... –

+0

@ Mark: en realidad para el problema del vendedor ambulante, se puede cuantificar la optimalidad de la solución encontrada por métodos no deterministas. Se garantiza que tendrá una solución dentro de XXX de la óptima (en términos de costo) después de los pasos NNN con probabilidad YYY, y tiene una fórmula que relaciona XXX, YYY y NNN. Es muy bueno en la práctica. –

+0

cierto - tiene la garantía de que la solución estará dentro de XXX de óptima - mi observación fue solo que podría tener suerte y llegar a la solución óptima - Estaba criticando su publicación original cuando dijo que "no" obtendría la solución óptima - es posible, pero ciertamente no apostaría en eso :-) –

4

Solo por diversión, probé la solución de programación dinámica precisa. Escrito en Python, debido a mi confianza suprema de que no debe optimizar hasta que tenga que hacerlo ;-)

Esto podría proporcionarle un comienzo, o bien una idea aproximada de qué tan cerca puede llegar antes de recurrir a la aproximación.

Código basa en http://en.wikipedia.org/wiki/Knapsack_problem#0-1_knapsack_problem, por lo tanto, el menos-que-informativos nombres de variables 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() 

Por simplicidad sólo estoy teniendo en cuenta los tamaños de archivo: una vez que tenga la lista de tamaños que desee utilizar, se pueden encontrar algunos nombres de archivo con esos tamaños buscando a través de una lista, por lo que no tiene sentido que se enrede nombres de archivo en el núcleo, parte lenta del programa. También estoy expresando todo en múltiplos del tamaño del bloque.

Como puede ver, he comentado el código que proporciona la solución real (en lugar del valor de la solución). Eso fue para ahorrar memoria: la forma correcta de almacenar la lista de archivos utilizados no es una lista en cada Solución, sino que cada punto de solución debe volver a la Solución de la que se derivó. Luego, puede calcular la lista de tamaños de archivo al final volviendo a la cadena, generando la diferencia entre los valores en cada paso.

Con una lista de 100 tamaños de archivo generados aleatoriamente en el rango 2000-6000 (estoy asumiendo 2k bloques, por lo que son archivos de tamaño 4-12MB), esto resuelve para W = 40K en 100 segundos en mi computadora portátil . Al hacerlo, calcula 2,6 millones de posibles soluciones 4M.

La complejidad es O (W * n), donde n es el número de archivos. Esto no contradice el hecho de que el problema es NP completo. Así que, al menos, me estoy acercando a una solución, y esto es solo en Python no optimizado.

Claramente, ahora se requiere algo de optimización, porque en realidad necesita ser resuelto para W = 4M (8GB DVD) y cuantos archivos tenga (digamos unos miles). Suponiendo que el programa puede tomar 15 minutos (comparable al tiempo requerido para escribir un DVD), eso significa que actualmente el rendimiento es corto en un factor de aproximadamente 10^3. Tenemos un problema que es bastante difícil de resolver de forma rápida y precisa en una PC, pero no más allá de los límites de la tecnología.

El uso de la memoria es la principal preocupación, ya que una vez que comenzamos a pulsar el botón vamos a reducir la velocidad, y si nos quedamos sin espacio de direcciones virtual nos encontramos en un problema porque tenemos que implementar nuestro propio almacenamiento en el disco . Mi ejecución de prueba alcanza un máximo de 600 MB. Si escribió el código en C en una máquina de 32 bits, cada "solución" tiene un tamaño fijo de 8 bytes. Por lo tanto, podría generar una matriz masiva de 2-D sin realizar ninguna asignación de memoria en el bucle, pero en 2 GB de RAM solo podría manejar W = 4M y n = 67. Vaya, los DVD están fuera. Sin embargo, podría resolver casi por completo los CD de bloques de 2-k: W = 350k da n = 766.

Editar: La sugerencia de MAK para calcular iterativamente de abajo hacia arriba, en lugar de recursivamente de arriba hacia abajo, debe reducir enormemente los requisitos de memoria. Primero calcule m (1, w) para todos 0 < = w < = W. Desde esta matriz, puede calcular m (2, w) para todos 0 < = w < = W. Entonces puede tirar todos los m (1, w) valores: no los necesitará para calcular m (3, w) etc.

Por cierto, sospecho que en realidad el problema que desea resolver podría ser el bin packing problem, en lugar de solo la pregunta de cómo obtener lo más cerca posible de llenar un DVD. Eso es, si tiene muchos archivos, desea escribirlos todos en DVD, usando el menor número de DVD posible. Hay situaciones en las que es muy fácil resolver el problema del embalaje de la basura, pero resolver este problema es difícil. Por ejemplo, supongamos que tiene discos de 8GB y 15GB de archivos pequeños. Va a llevar un tiempo buscar para encontrar la coincidencia más cercana posible a 8GB, pero el problema del empaquetamiento de bandejas sería trivialmente resuelto simplemente colocando aproximadamente la mitad de los archivos en cada disco; no importa exactamente cómo los divide porque está va a perder 1GB de espacio haga lo que haga.

Dicho todo esto, hay heurísticas extremadamente rápidas que dan resultados decentes la mayor parte del tiempo. Lo más simple es ir a través de la lista de archivos (quizás en orden decreciente de tamaño), e incluir cada archivo si cabe, excluirlo de lo contrario. Solo necesita retroceder a algo lento si las soluciones aproximadas rápidas no son "lo suficientemente buenas", para su elección de "suficiente".

+0

+1. Posibles optimizaciones (para el OP): utilice un DP de abajo hacia arriba en lugar de recursión, o al menos reemplace el 'dict' con un arreglo 2D o' list' de 'list's. – MAK

+0

Y otra optimización: no cree una nueva lista 'newarray', use la misma lista pero pase un índice. Junto con una matriz 2-D para 'm', y asumiendo un cambio a un lenguaje como C, que elimina la última asignación de memoria de' compute'. –

+0

@MAK: No estoy * demasiado * preocupado por la recursión, ya que solo va 'n' profundo. Para n> = 1000, eso significa llamar 'sys.setrecursionlimit' en Python. –

0

Si está buscando una heurística razonable, y el objetivo es minimizar el número de discos requeridos, aquí hay uno simple que podría considerar. Es similar a uno que utilicé recientemente para un problema de tienda de trabajo. Pude compararlo con el optima conocido y encontré que proporcionaba asignaciones que eran óptimas o extremadamente cercanas a ser óptimas.

Supongamos que B es el tamaño de todos los archivos combinados y C es la capacidad de cada disco. Entonces necesitarás al menos n discos de redondeo (B/C). Intenta ajustar todos los archivos en n discos. Si puede hacerlo, ha terminado y tiene una solución óptima. De lo contrario, intente ajustar todos los archivos en n + 1 discos. Si puede hacerlo, tiene una solución heurística; de lo contrario, intente ajustar los archivos en n + 2 discos, y así sucesivamente, hasta que pueda hacerlo.

Para cualquier asignación dada de archivos a los discos a continuación (que pueden exceder algunas capacidades de disco), que sea el tamaño combinado de los archivos asignados al disco i, y t = max si. Hemos terminado cuando t < = C.

Primero, ordene (e indexe) los archivos más grandes a los más pequeños.

Para m> = n discos,

  1. asignar los archivos a los discos en un camino de vuelta-en-sucesivamente: 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, y así sucesivamente hasta que se hayan asignado todos los archivos, sin importar la capacidad del disco. Si t < = C terminamos (y la asignación es óptima si m = n); de lo contrario, ve al # 2.

  2. Intenta reducir t moviendo un archivo de un disco i con si = t a otro disco, sin aumentar t. Continúe haciendo esto hasta t < = C, en cuyo caso hemos terminado (y la asignación es óptima si m = n), o t no puede reducirse más, en cuyo caso vaya al n. ° 3.

  3. Intenta reducir t realizando intercambios por parejas entre discos. Continúe haciendo esto hasta t < = C, en cuyo caso hemos terminado (y la asignación es óptima si m = n), o t no puede reducirse más con intercambios por pares. En este último caso, repita el n. ° 2, a menos que no se haya realizado ninguna mejora la última vez que se repitió el n. ° 2, en cuyo caso se incrementan m por uno y se repite n. ° 1.

En el n. ° 2 y n. ° 3 existen, por supuesto, diferentes formas de encargar posibles reasignaciones e intercambios por parejas.

Cuestiones relacionadas