En informática , el problema de la suma máxima de subconjuntos , también conocido como el problema de la suma máxima de segmentos , es la tarea de encontrar un subconjunto contiguo con la suma más grande, dentro de una matriz unidimensional dada A[1...n] de números. Se puede resolver en tiempo y espacio.
Formalmente, la tarea es encontrar índices y con , tales que la suma
es lo más grande posible. (Algunas formulaciones del problema también permiten considerar el subconjunto vacío; por convención, la suma de todos los valores del subconjunto vacío es cero). Cada número en el conjunto de entrada A podría ser positivo, negativo o cero. [1]
Por ejemplo, para la matriz de valores [−2, 1, −3, 4, −1, 2, 1, −5, 4], la submatriz contigua con la suma más grande es [4, −1, 2, 1], con suma 6.
Algunas propiedades de este problema son:
Aunque este problema se puede resolver utilizando varias técnicas algorítmicas diferentes, incluyendo fuerza bruta, [2] dividir y vencer, [3] programación dinámica, [4] y reducción a caminos más cortos, un algoritmo simple de una sola pasada conocido como algoritmo de Kadane lo resuelve de manera eficiente.
El problema del subarreglo máximo fue propuesto por Ulf Grenander en 1977 como un modelo simplificado para la estimación de máxima verosimilitud de patrones en imágenes digitalizadas. [5]
Grenander buscaba encontrar un subarreglo rectangular con suma máxima, en un arreglo bidimensional de números reales. Un algoritmo de fuerza bruta para el problema bidimensional se ejecuta en tiempo O ( n 6 ); debido a que esto era prohibitivamente lento, Grenander propuso el problema unidimensional para comprender mejor su estructura. Grenander derivó un algoritmo que resuelve el problema unidimensional en tiempo O ( n 2 ), [nota 1] mejorando el tiempo de ejecución de fuerza bruta de O ( n 3 ). Cuando Michael Shamos se enteró del problema, ideó de la noche a la mañana un algoritmo de divide y vencerás O ( n log n ) para él. Poco después, Shamos describió el problema unidimensional y su historia en un seminario de la Universidad Carnegie Mellon al que asistió Jay Kadane , quien diseñó en un minuto un algoritmo de tiempo O ( n ), [5] [6] [7] que es lo más rápido posible. [nota 2] En 1982, David Gries obtuvo el mismo algoritmo de tiempo O ( n ) aplicando la "estrategia estándar" de Dijkstra ; [8] en 1989, Richard Bird lo derivó mediante manipulación puramente algebraica del algoritmo de fuerza bruta utilizando el formalismo de Bird-Meertens . [9]
La generalización bidimensional de Grenander se puede resolver en tiempo O( n 3 ) ya sea utilizando el algoritmo de Kadane como una subrutina, o a través de un enfoque de divide y vencerás. Tamaki y Tokuyama (1998) y Takaoka (2002) propusieron algoritmos ligeramente más rápidos basados en la multiplicación de matrices de distancia . Hay cierta evidencia de que no existe un algoritmo significativamente más rápido; un algoritmo que resuelva el problema de subarreglo máximo bidimensional en tiempo O( n 3−ε ), para cualquier ε>0, implicaría un algoritmo igualmente rápido para el problema de caminos más cortos de todos los pares . [10]
Los problemas de submatrices máximas surgen en muchos campos, como el análisis de secuencias genómicas y la visión artificial .
El análisis de secuencias genómicas emplea algoritmos de subarreglo máximo para identificar segmentos biológicos importantes de secuencias de proteínas que tienen propiedades inusuales, asignando puntajes a puntos dentro de la secuencia que son positivos cuando está presente un motivo a reconocer y negativos cuando no lo está, y luego buscando el subarreglo máximo entre estos puntajes. Estos problemas incluyen segmentos conservados, regiones ricas en GC, repeticiones en tándem, filtro de baja complejidad, dominios de unión al ADN y regiones de alta carga. [11]
En la visión artificial , las imágenes de mapa de bits generalmente constan solo de valores positivos, para los cuales el problema de subarreglo máximo es trivial: el resultado es siempre el arreglo completo. Sin embargo, después de restar un valor umbral (como el valor de píxel promedio) de cada píxel, de modo que los píxeles superiores al promedio sean positivos y los píxeles inferiores al promedio sean negativos, el problema de subarreglo máximo se puede aplicar a la imagen modificada para detectar áreas brillantes dentro de ella. [12]
El algoritmo de Kadane escanea la matriz dada de izquierda a derecha. En el paso n, calcula la submatriz con la suma más grande que termina en ; esta suma se mantiene en la variable . [nota 3]
Además, calcula la submatriz con la suma más grande en cualquier parte de , se mantiene en la variable , [nota 4]
y se obtiene fácilmente como el máximo de todos los valores de vistos hasta ahora, cf. línea 7 del algoritmo.current_sum
best_sum
current_sum
Como invariante de bucle , en el paso n, el valor anterior de contiene el máximo sobre toda la suma . Por lo tanto, [nota 5]
es el máximo sobre toda la suma . Para extender el último máximo para cubrir también el caso , es suficiente considerar también el subarreglo singleton . Esto se hace en la línea 6 asignando como el nuevo valor de , que después contiene el máximo sobre toda la suma .current_sum
current_sum
current_sum
current_sum
Por tanto, el problema se puede resolver con el siguiente código, [13] expresado en Python .
def max_subarray ( números ): """Encuentra la suma más grande de cualquier submatriz contigua.""" mejor_suma = float ( '-inf' ) suma_actual = 0 para x en números : suma_actual = máx ( x , suma_actual + x ) mejor_suma = max ( mejor_suma , suma_actual ) devolver mejor_suma
Si la entrada no contiene ningún elemento positivo, el valor devuelto es el del elemento más grande (es decir, el valor más cercano a 0), o infinito negativo si la entrada estaba vacía. Para que sea correcto, se debe generar una excepción cuando la matriz de entrada esté vacía, ya que una matriz vacía no tiene un máximo de submatriz no vacía. Si la matriz no está vacía, se podría usar su primer elemento en lugar de infinito negativo, si es necesario para evitar mezclar valores numéricos y no numéricos.
El algoritmo se puede adaptar al caso que permite submatrices vacías o realizar un seguimiento de los índices inicial y final de la submatriz máxima.
Este algoritmo calcula el subarreglo máximo que termina en cada posición a partir del subarreglo máximo que termina en la posición anterior, por lo que puede verse como un caso trivial de programación dinámica .
El algoritmo original de Kadane resuelve la variante del problema cuando se admiten submatrices vacías. [4] [7]
Esta variante devolverá 0 si la entrada no contiene elementos positivos (incluso cuando la entrada está vacía). Se obtiene mediante dos cambios en el código: en la línea 3, best_sum
debe inicializarse a 0 para tener en cuenta la submatriz vacía
mejor_suma = 0 ;
y la línea 6 del bucle for current_sum
debe actualizarse como max(0, current_sum + x)
. [nota 6]
suma_actual = máx ( 0 , suma_actual + x )
Como invariante de bucle , en el paso n, el valor anterior de contiene el máximo sobre toda la suma . [nota 7]
Por lo tanto,
es el máximo sobre toda la suma . Para extender el último máximo para cubrir también el caso , es suficiente considerar también el subarreglo vacío . Esto se hace en la línea 6 asignando como el nuevo valor de , que después contiene el máximo sobre toda la suma . El código C / Frama-C verificado por máquina de ambas variantes se puede encontrar aquí.current_sum
current_sum
current_sum
current_sum
El algoritmo también se puede modificar para realizar un seguimiento de los índices inicial y final de la submatriz máxima.
Debido a la forma en que este algoritmo utiliza subestructuras óptimas (el subarreglo máximo que termina en cada posición se calcula de manera simple a partir de un subproblema relacionado pero más pequeño y superpuesto: el subarreglo máximo que termina en la posición anterior), este algoritmo puede verse como un ejemplo simple/trivial de programación dinámica .
La complejidad en tiempo de ejecución del algoritmo de Kadane es y su complejidad espacial es . [4] [7]
Se pueden plantear problemas similares para matrices de dimensiones superiores, pero sus soluciones son más complicadas; véase, por ejemplo, Takaoka (2002). Brodal y Jørgensen (2007) demostraron cómo encontrar las k sumas de subarreglos más grandes en una matriz unidimensional, en el límite de tiempo óptimo .
La suma máxima de k submatrices disjuntas también se puede calcular en el límite de tiempo óptimo . [14]
MaxEndingHere
en Bentley (1989) y c
en Gries (1982)MaxSoFar
en Bentley (1989) y s
en Gries (1982)x
x
en lugar de 0
en la versión anterior sin submatrices vacías logra mantener su bucle invariante al comienzo del paso n.current_sum