Algoritmo de búsqueda de carpetas

votos
0

No estoy seguro de si este es el tipo de pregunta habitual que se hace aquí, o si obtendré alguna respuesta a esta, pero estoy buscando un enfoque de pseudocódigo para generar registros de vinculación de DB a partir de una estructura de carpeta que contiene una imagen archivos.

Tengo un conjunto de carpetas, estructuradas como Folllows:

+-make_1/
  | +--model_1/
  |    +-default_version/
  |    |   +--1999
  |    |   +--2000
  |    |   |   +--image_01.jpg
  |    |   |   +--image_02.jpg
  |    |   |   +--image_03.jpg
  |    |   |   ...
  |    |   +--2001
  |    |   +--2002
  |    |   +--2003
  |    |   ...
  |    |   +--2009
  |    +--version_1/
  |    |   +--1999
  |    |   ...
  |    |   +--2009
  |    +--version_2/
  |    |   +--1999
  |    |   +--2000
  |    |   +--2001
  |    |   |   +--image_04.jpg
  |    |   |   +--image_05.jpg
  |    |   |   +--image_06.jpg
  |    |   |   ...
  |    |   +--2002
  |    |   +--2003
  |    |   |   +--image_07.jpg
  |    |   |   +--image_08.jpg
  |    |   |   +--image_09.jpg
  |    |   ...
  |    |   +--2009
  ...  ... ...  

En esencia, representa posibles imágenes para vehículos, por año a partir de 1999.

Las marcas y los modelos (p. Ej., Marca: Alfa Romeo, Modelo: 145) vienen en diversos ajustes o versiones. Cada versión o recorte se puede encontrar en una serie de vehículos que se verán iguales, pero que tienen diferencias en el tipo de combustible o la capacidad del motor.

Para guardar la duplicación, la estructura de carpetas anterior hace uso de una carpeta predeterminada ... Y las imágenes aparecen para la versión predeterminada desde 2000 en adelante. Necesito producir la tabla de enlaces para cada versión, en función de si tienen sus propias imágenes primordiales o si utilizan la versión predeterminada ...

Por ejemplo, version_1 no tiene archivos de imagen, por lo que necesito hacer enlaces a las imágenes predeterminadas, comenzando en 2000 y continuando hasta 2009.

La versión 2, por otro lado, comienza utilizando las imágenes predeterminadas en 2000, pero luego usa dos conjuntos nuevos primero para 2001-2002 y luego 2003-2009. La lista de enlaces necesarios son por lo tanto ...

version    start     end   file_name
=======    =====   =====   =========
version_1   2000    2009   image_01.jpg
version_1   2000    2009   image_02.jpg
version_1   2000    2009   image_03.jpg
...
version_2   2000    2001   image_01.jpg
version_2   2000    2001   image_02.jpg
version_2   2000    2001   image_03.jpg
version_2   2001    2003   image_04.jpg
version_2   2001    2003   image_05.jpg
version_2   2001    2003   image_06.jpg
version_2   2003    2009   image_07.jpg
version_2   2003    2009   image_08.jpg
version_2   2003    2009   image_09.jpg
...

(El valor predeterminado es solo eso: un marcador de posición y no se requieren enlaces).

En este momento estoy revisando las carpetas, construyendo matrices y recortando la grasa al final. Me preguntaba si había un atajo, usando algún tipo de enfoque de procesamiento de texto. Hay alrededor de 45,000 carpetas, la mayoría de las cuales están vacías :-)

Publicado el 05/07/2009 a las 21:43
fuente por usuario
En otros idiomas...                            


1 respuestas

votos
1

Aquí hay un pseudocódigo de Python, bastante cercano al ejecutable (necesita importaciones adecuadas y una def para una función de escritura que hará la escritura real, ya sea en un archivo intermedio, DB, CSV, lo que sea):

# first, collect all the data in a dict of dicts of lists
# first key is version, second key is year (only for non-empty years)

tree = dict()
for root, dirs, files in os.walk('make_1/model_1'):
    head, tail = os.path.split(root)
    if dirs:
       # here, tail is a version
       tree[tail] = dict
    elif files:
       # here, tail is a year
       tree[os.path.basename(head)][tail] = files

# now specialcase default_version
default_version = tree.pop('default_version')
# determine range of years; rule is quite asymmetrical:
#   for min, only years with files in them count
min_year = min(d for d in default_version if default_version[d])
#   for max, all years count, even if empty
max_year = max(default_version)

for version, years in tree.iteritems():
    current_files = default_version[min_year]
    years.append(max_year + 1)
    y = min_year
    while years:
        next_change = min(years)
        if y < next_change:
            for f in current_files:
                writerow(version, y, next_change-1, f)
        y = next_change
        current_files = years.pop(y)

Una ambigüedad en la especificación y el ejemplo es si es posible que default_version cambie el conjunto de archivos en algunos años; en este caso, supongo que eso no sucede (solo las versiones específicas cambian de esa manera, la versión predeterminada siempre incluye un conjunto) de archivos).

Si este no es el caso, ¿qué sucede si la versión predeterminada cambia en años (digamos) 1999 y 2003, y la versión 1 cambia en 2001 y 2005? ¿Qué archivos debería usar la versión 1 para 03 y 04, los nuevos en la versión predeterminada? , o los especificó en 01?

En la versión más complicada de la especificación (donde tanto default_version como una específica pueden cambiar, con el cambio más reciente teniendo prioridad, y si tanto el específico como el predeterminado cambian en el mismo año luego el específico que toma precedencia) uno necesita obtener todo el secuencia del "próximo año de cambio", para cada versión específica, mediante una cuidadosa "fusión de prioridades" de las secuencias de años de cambio para la versión predeterminada y específica, en lugar de simplemente usar years(la secuencia de cambios en la versión específica) como hago aquí - y cada año de cambio colocado en la secuencia debe estar asociado con el conjunto apropiado de archivos, por supuesto.

Entonces, si la especificación exacta puede ser expresada, hasta casos de la esquina, puedo mostrar cómo hacer la fusión necesaria modificando este pseudocódigo: prefiero no hacer el trabajo hasta que se aclaren las especificaciones exactas, porque si el ¡las especificaciones son de hecho más simples, el trabajo sería innecesario! -)

Editar : como se aclaró un nuevo comentario, las especificaciones exactas son de hecho las más complejas, por lo que debemos hacer la fusión de manera apropiada. Entonces, el ciclo al final de la respuesta simplista anterior cambia a:

for version, years_dict in tree.iteritems():
    # have years_dict override default_version when coincident
    merged = dict(default_version, **years_dict)
    current_files = merged.pop(min_year)
    merged[max_year + 1] = None
    y = min_year
    while merged:
        next_change = min(merged)
        for f in current_files:
            writerow(version, y, next_change-1, f)
        y = next_change
        current_files = merged.pop(y)

El cambio clave es la merged = dict(...línea: en Python, esto significa fusionar un nuevo dict (un dict es un mapeo genérico, típicamente se llamaría un hashmap en otros idiomas) que es la suma, o fusión, de default_versiony years_dict, pero cuando la clave está presente en ambos, el valor de years_dicttoma prioridad, que cumple la condición clave para un año que está presente (es decir, es un año con un cambio en los archivos) en ambos.

Después de eso, es sencillo: anydict.pop (somekey) devuelve el valor correspondiente a la clave (y también lo elimina de anydict); min (anydict) devuelve la clave mínima en el diccionario. Observe la expresión "centinela" en merged[max_year + 1] = None: esto indica que el año "uno después del máximo" siempre se considera como un año de cambio (con un valor de marcador ficticio Ninguno), de modo que el último conjunto de filas siempre se escribe correctamente (con un año máximo de max_year + 1 - 1, es decir, exactamente max_year, como se desee).

¡Este algoritmo no es eficiente al máximo, simplemente lo más simple! Lo estamos haciendo min(merged)una y otra vez, convirtiéndolo en O (N al cuadrado). Creo que podemos permitirnos eso porque cada uno mergeddebería tener como mínimo una docena de años de cambio, pero un purista se estremecería. Por supuesto, podemos presentar una solución O (N logN): simplemente clasifique los años de una vez por todas y recorra esa secuencia para obtener los valores sucesivos next_change. Solo para completar ...

default_version[max_year + 1] = None

for version, years_dict in tree.iteritems():
    merged = dict(default_version, **years_dict)
    for next_change in sorted(merged):
        if next_change > min_year:
            for f in merged[y]:
                writerow(version, y, next_change-1, f)
        y = next_change

Aquí se sortedda una lista con las claves mergeden orden ordenado, y he cambiado a la forinstrucción para recorrer esa lista de principio a fin (y una instrucción if para no dar salida a nada la primera vez). El centinela ahora se pone en default_version (por lo que está fuera del ciclo, para otra ligera optimización). Es divertido ver que esta versión optimizada (esencialmente porque funciona a un nivel de abstracción ligeramente superior) resulta ser más pequeña y simple que las anteriores ;-).

Respondida el 05/07/2009 a las 22:57
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more