programacion operaciones metodo investigacion importancia economia dinamica dimensionalidad deterministica algorithm partitioning dynamic-programming

algorithm - operaciones - ¿Cómo entender la solución de programación dinámica en particiones lineales?



programacion dinamica matlab (2)

Estoy luchando por comprender la solución de programación dinámica para el problema de partición lineal. Estoy leyendo el Manual de diseño de algoritmos y el problema se describe en la sección 8.5. He leído la sección innumerables veces, pero simplemente no la recibo. Creo que es una explicación pobre (lo que he leído hasta ahora ha sido mucho mejor), pero no he podido entender el problema lo suficientemente bien como para buscar una explicación alternativa. Enlaces a mejores explicaciones bienvenidos!

Encontré una página con texto similar al libro (tal vez de la primera edición del libro): The Partition Problem .

Primera pregunta: en el ejemplo del libro, las particiones se ordenan de menor a mayor. ¿Es esto solo una coincidencia? Por lo que puedo ver, el orden de los elementos no es significativo para el algoritmo.

Esta es mi comprensión de la recursión:

Permite usar la siguiente secuencia y dividirla en 4:

{S1...Sn} = 100 150 200 250 300 350 400 450 500 k = 4

Segunda pregunta: Así es como creo que comenzará la recursión: ¿lo he entendido correctamente?

La primera recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 350 | 400 | 450 | 500 //1 partition to go 100 150 200 250 300 | 350 | 400 | 450 | 500 //done

La segunda recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 350 | 400 | 450 | 500 //1 partition to go 100 150 200 250 | 300 350 | 400 | 450 | 500 //done

La tercera recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 350 | 400 | 450 | 500 //1 partition to go 100 150 200 | 250 300 350 | 400 | 450 | 500 //done

La cuarta recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 350 | 400 | 450 | 500 //1 partition to go 100 150 | 200 250 300 350 | 400 | 450 | 500 //done

La quinta recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 350 | 400 | 450 | 500 //1 partition to go 100 | 150 200 250 300 350 | 400 | 450 | 500 //done

La sexta recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 | 350 400 | 450 | 500 //1 partition to go 100 150 200 250 | 300 | 350 400 | 450 | 500 //done

La 7ma recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 | 350 400 | 450 | 500 //1 partition to go 100 150 200 | 250 300 | 350 400 | 450 | 500 //done

La octava recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 | 350 400 | 450 | 500 //1 partition to go 100 150 | 200 250 300 | 350 400 | 450 | 500 //done

La novena recursión es:

100 150 200 250 300 350 400 450 | 500 //3 partition to go 100 150 200 250 300 350 400 | 450 | 500 //2 partition to go 100 150 200 250 300 | 350 400 | 450 | 500 //1 partition to go 100 | 150 200 250 300 | 350 400 | 450 | 500 //done

etc ...

Aquí está el código tal como aparece en el libro:

partition(int s[], int n, int k) { int m[MAXN+1][MAXK+1]; /* DP table for values */ int d[MAXN+1][MAXK+1]; /* DP table for dividers */ int p[MAXN+1]; /* prefix sums array */ int cost; /* test split cost */ int i,j,x; /* counters */ p[0] = 0; /* construct prefix sums */ for (i=1; i<=n; i++) p[i]=p[i-1]+s[i]; for (i=1; i<=n; i++) m[i][3] = p[i]; /* initialize boundaries */ for (j=1; j<=k; j++) m[1][j] = s[1]; for (i=2; i<=n; i++) /* evaluate main recurrence */ for (j=2; j<=k; j++) { m[i][j] = MAXINT; for (x=1; x<=(i-1); x++) { cost = max(m[x][j-1], p[i]-p[x]); if (m[i][j] > cost) { m[i][j] = cost; d[i][j] = x; } } } reconstruct_partition(s,d,n,k); /* print book partition */ }

Pregunta sobre el algoritmo:

  1. ¿Qué valores se almacenan en m y d ?
  2. ¿Qué significa ''costo''? ¿Es simplemente el total de los valores de los elementos dentro de una partición? ¿O hay algún significado adicional más sutil?

Tenga en cuenta que hay un pequeño error en la explicación del algoritmo en el libro, busque en la errata del texto "(*) Page 297".

Sobre tus preguntas:

  1. No, los elementos no necesitan ordenarse, solo son contiguos (es decir, no puede reorganizarlos)
  2. Creo que la forma más fácil de visualizar el algoritmo es trazando a mano el procedimiento reconstruct_partition , usando la tabla de la derecha en la figura 8.8 como guía
  3. En el libro se establece que m [i] [j] es "el costo mínimo posible sobre todas las divisiones de {s1, s2, ..., si}" en j rangos, donde el costo de una partición es la mayor suma de elementos en una de sus partes ". En otras palabras, es el" máximo de sumas más pequeño ", si indulta el abuso de la terminología. Por otro lado, d [i] [j] almacena la posición del índice que se utilizó para hacer una partición para un par dado i, j como se definió anteriormente
  4. Para el significado de "costo", ver la respuesta anterior

Editar:

Aquí está mi implementación del algoritmo de partición lineal. Está basado en el algoritmo de Skiena, pero de forma pitónica; y devuelve una lista de las particiones.

from operator import itemgetter def linear_partition(seq, k): if k <= 0: return [] n = len(seq) - 1 if k > n: return map(lambda x: [x], seq) table, solution = linear_partition_table(seq, k) k, ans = k-2, [] while k >= 0: ans = [[seq[i] for i in xrange(solution[n-1][k]+1, n+1)]] + ans n, k = solution[n-1][k], k-1 return [[seq[i] for i in xrange(0, n+1)]] + ans def linear_partition_table(seq, k): n = len(seq) table = [[0] * k for x in xrange(n)] solution = [[0] * (k-1) for x in xrange(n-1)] for i in xrange(n): table[i][0] = seq[i] + (table[i-1][0] if i else 0) for j in xrange(k): table[0][j] = seq[0] for i in xrange(1, n): for j in xrange(1, k): table[i][j], solution[i-1][j-1] = min( ((max(table[x][j-1], table[i][0]-table[x][0]), x) for x in xrange(i)), key=itemgetter(0)) return (table, solution)


Implementé el algoritmo de Óscar López en PHP. Por favor, siéntete libre de usarlo cuando lo necesites.

/** * Example: linear_partition([9,2,6,3,8,5,8,1,7,3,4], 3) => [[9,2,6,3],[8,5,8],[1,7,3,4]] * @param array $seq * @param int $k * @return array */ protected function linear_partition(array $seq, $k) { if ($k <= 0) { return array(); } $n = count($seq) - 1; if ($k > $n) { return array_map(function ($x) { return array($x); }, $seq); } list($table, $solution) = $this->linear_partition_table($seq, $k); $k = $k - 2; $ans = array(); while ($k >= 0) { $ans = array_merge(array(array_slice($seq, $solution[$n - 1][$k] + 1, $n - $solution[$n - 1][$k])), $ans); $n = $solution[$n - 1][$k]; $k = $k - 1; } return array_merge(array(array_slice($seq, 0, $n + 1)), $ans); } protected function linear_partition_table($seq, $k) { $n = count($seq); $table = array_fill(0, $n, array_fill(0, $k, 0)); $solution = array_fill(0, $n - 1, array_fill(0, $k - 1, 0)); for ($i = 0; $i < $n; $i++) { $table[$i][0] = $seq[$i] + ($i ? $table[$i - 1][0] : 0); } for ($j = 0; $j < $k; $j++) { $table[0][$j] = $seq[0]; } for ($i = 1; $i < $n; $i++) { for ($j = 1; $j < $k; $j++) { $current_min = null; $minx = PHP_INT_MAX; for ($x = 0; $x < $i; $x++) { $cost = max($table[$x][$j - 1], $table[$i][0] - $table[$x][0]); if ($current_min === null || $cost < $current_min) { $current_min = $cost; $minx = $x; } } $table[$i][$j] = $current_min; $solution[$i - 1][$j - 1] = $minx; } } return array($table, $solution); }