Encontrar formas en matriz 2D, luego optimizar

11

Acabo de recibir una imagen ... La imagen de abajo de mi juego muestra algunos bloques oscuros, que han sido reconocidos como parte de una forma de "T". Como se puede ver, el código ha oscurecido los bloques con las manchas rojas y no ha visto las formas "T" con los contornos verdes.

Se encontraron patrones deseados, pero aún no optimizados

Mi código recorre x / y, marca bloques como se usan, gira la forma, repite, cambia de color, repite.

He comenzado a tratar de arreglar esta comprobación con gran temor. La idea actual es:

  • recorrer la cuadrícula y tomar nota de todas las ocurrencias de patrones (NO marcar bloques como se usan), y ponerlos en una matriz
  • Vuelva a recorrer la cuadrícula, esta vez observando qué bloques están ocupados por qué patrones y, por lo tanto, cuáles están ocupados por múltiples patrones.
  • recorriendo nuevamente la cuadrícula, esta vez observando qué patrones obstruyen qué patrones

Eso se siente bien ... ¿Qué hago ahora?

Yo creo que tendría que

  • pruebe varias combinaciones de formas en conflicto, comenzando con aquellas que obstruyen la mayoría de los otros patrones primero. ¿Cómo me acerco a este?
  • use el racional que dice que tengo 3 formas en conflicto que ocupan 8 bloques, y las formas son de 4 bloques cada una, por lo tanto, solo puedo tener un máximo de dos formas.

(También tengo la intención de incorporar otras formas, y probablemente habrá una ponderación de puntaje que deberá tenerse en cuenta al pasar por las formas en conflicto, pero eso puede ser otro día)

No creo que sea un problema de embalaje, pero no estoy seguro de qué buscar. Espero que tenga sentido, gracias por tu ayuda

EDITAR A pesar de la claridad de la pregunta, todo el mundo parece haber entendido, sí,

Quiero encontrar las formas máximas de "T" dentro de cada color

(porque si te diera puntos por dos y hubieras hecho tres, estarías un poco molesto)

Ensamblador
fuente
Un algoritmo codicioso podría ser dividir el tablero en colecciones de bloques unidos. Luego, para cada colección, puede intentar rellenar con formas y darle una puntuación al relleno dependiendo de la cantidad de bloques restantes que no se oscurecerían. Algo así me hace pensar en en.wikipedia.org/wiki/Knapsack_problem .
Jonathan Connell
2
Creo que falta algo en la pregunta. ¿Desea hacer un algoritmo que encuentre tantos grupos en forma de "T" como sea posible?
Markus von Broady
Si te entiendo, entonces estás yendo por el camino correcto. No eres muy claro y me encantaría que pudieras dar más detalles.
AturSams

Respuestas:

3

Déjame ver si acerté, los bloques marcados en rojo, eran azules y el algoritmo encontró una forma de T y los marcó en rojo, ¿es correcto? Su objetivo es encontrar tantas formas de T como sea posible con bloques del mismo color, correcto hasta ahora, espero. Actualmente los marca una vez que los encuentra y eso disminuye la utilidad del algoritmo (ya que podría faltar la solución óptima). Está planeando buscar todas las formas y luego elegir cuáles usar y cuáles no. ¿Estoy en lo correcto hasta ahora? Porque desea maximizar la cantidad de bloques que están contenidos dentro de las formas T cuando el algoritmo está listo.

Si estoy en lo correcto, la siguiente es la solución óptima para su situación en mi opinión.

Utilizaremos la programación lineal entera.

Creo que usé este en el pasado:

http://sourceforge.net/projects/lpsolve/

http://lpsolve.sourceforge.net/5.5/Java/README.html

(Puede hacer que funcione con muchos lenguajes, lo usé con PHP, Java y C)

Lo que haremos es registrar cada forma de T posible en el tablero y luego usar ILP para maximizar la cantidad de bloques que están cubiertos. ILP es exponencialmente complejo. Teniendo en cuenta el tamaño de su placa, eso no será un problema. He realizado preguntas mínimas / máximas mucho más complicadas en gráficos con ILP y solo tardó una fracción de segundo en completarse y hasta 30-90 segundos con cientos de vértices (en su caso, cae en la fracción de un segundo).

Lo que recomendaría hacer:

  1. Encuentra todas las formas de línea posibles
  2. Encuentra todas las intersecciones entre formas de línea del mismo color
  3. Encuentra todas las formas de T posibles, buscando en todas las intersecciones.
  4. Defina una variable booleana en el problema lineal para cada forma de T ( 0 <= Bi <= 1) Dado que los valores son enteros, eso deja 0 o 1.
  5. Establezca las condiciones para cada par de formas T que se cruzan ( Bi + Bj <= 1)
  6. La función objetivo será (suma de bloques en forma de "T" (i) * Bi)
  7. Ejecute el solucionador y oscurezca las formas T donde los booleanos correspondientes del solucionador fueron 1 en la solución óptima.

Este es un conocimiento valioso, usé solucionadores lineales a menudo para proyectos de trabajo.

ILP es básicamente una forma de resolver problemas de selección en los que desea alcanzar un máximo o un mínimo para alguna función lineal.

Puede leer más aquí, el uso de la programación lineal de enteros y la programación lineal es lo mismo para el programador, solo que Integer es mucho más complejo para la computadora, lo que puede resultar en largos tiempos de ejecución. No en su caso, es muy simple y solo debería tomar menos de milisegundos en el peor de los casos.

Supongo que puedes leer más aquí:

http://en.wikipedia.org/wiki/Integer_linear_programming#Integer_unknowns

Esto lo explica bien:

http://fisher.osu.edu/~croxton_4/tutorial/

Básicamente es un solucionador de problemas de decisión, cómo tomar decisiones que maximicen el resultado que desea. Esto asume que la función que juzga el resultado es lineal, que en su caso actual específico es. La función que juzga el resultado en este caso, resume los bloques de todas las formas T que decidió oscurecer.

Matemáticamente, cómo establecer las variables: en nuestro caso actual Booleanos (oscurecí la forma de T con índice i o no) a los valores óptimos para maximizar el resultado que queremos: oscurecer tantos bloques como sea posible sin oscurecer las formas de T que se cruzan. Siempre que el resultado que desee se pueda calcular con una función lineal cuando tenga todas las variables establecidas, lo resolverá. En nuestro caso, verificamos qué formas de T hemos oscurecido y sumamos los bloques que cubren.

ingrese la descripción de la imagen aquí

Sé que esto no es trivial, así que si eliges dar el salto, siéntete libre de comentar y daré más detalles.

AturSams
fuente
Gracias Arthur por tu ayuda. Puede tomar un par de lecturas para digerir. Y sí, entendiste el problema correctamente. Me interesaría mucho si tuviera que elaborar (no, no, no es trivial), ¡pero esto debería ayudarme a llegar a donde voy!
Ensamblador
¿Qué idioma estás usando para la implementación?
AturSams
actionscript 3! ¡El favorito de todos!
Ensamblador
igual que aquí. Escribiré una implementación en as3 y la subiré a un github para descargarla con comentarios, trabajando paso a paso. Puedo hacerlo más tarde hoy
AturSams
¿Tiene alguna área específica de 1 a 7 donde le gustaría que agregue más comentarios o más detalles? Por cierto, buenas noticias para los amantes de AS3, Adobe lanzó FlasCC que admite C ++ para que podamos usar solucionadores lineales existentes con facilidad. :)
AturSams
4

Una vez que tenga una lista de todas las formas de T (posiblemente superpuestas) que ocurren en su cuadrícula, lo que le queda es un problema de empaquetado máximo .

En general, este es un problema NP-completo. Sin embargo, su cuadrícula es lo suficientemente pequeña (y generalmente se divide en subproblemas independientes aún más pequeños) que puede ser factible obtener soluciones exactas.


Anexo: Aquí hay un algoritmo de búsqueda de retroceso básico que podría ser útil:

function max_packing_recursive ( set A, set S, set M ):
    if |M| < |S| then let M = S;
    for each shape X in A do:
        remove X from A;
        let B = A;
        remove all shapes that intersect with X from B;
        if |M| < |B| + |S| + 1 then:        // upper bound
            let M = max_packing_recursive( B, S + {X}, M );
        end if
        if |M| >= |A| + |S| then return M;  // shortcut
    end for
    return M;
end function

function max_packing( set A ):
    return max_packing_recursive( A, {}, {} );
end function

Aquí, {X, Y, Z}denota el conjunto que contiene los elementos X, Yy Z(con {}ser el conjunto vacío), y |Q|denota el tamaño del conjunto Q.

En la función recursiva, el conjunto Acontiene las formas disponibles para la solución restante, Scontiene las formas en el candidato de la solución actual y Mes la solución máxima hasta ahora (que es posible que desee almacenar como una variable global en lugar de devolverla de nuevo al cadena de llamadas). La optimización importante está en la línea marcada con // upper bound, que poda las ramas del árbol de búsqueda que no pueden devolver una solución mejor que M.

(En realidad, dado que sabemos que cada forma de T contiene exactamente cuatro sitios, se podría obtener un límite superior mucho mejor reemplazando |B|con el número de sitios distintos cubiertos por las formas en B, dividido por cuatro y redondeado hacia abajo (y de manera similar para |A|el línea marcada con // shortcut). Sin embargo, el algoritmo descrito anteriormente funciona para colecciones arbitrarias de formas).

Una posible optimización adicional, que no he implementado anteriormente, sería verificar al comienzo de la función recursiva si se Adivide en múltiples subconjuntos que son independientes, en el sentido de que no se superponen formas en diferentes subconjuntos, y si es así, aplique el algoritmo para cada uno de los subconjuntos por separado. (En cualquier caso, definitivamente querrá hacer esto al menos una vez en el nivel superior antes de llamar al algoritmo recursivo). Clasificar las formas de manera Aapropiada antes de recorrerlas, por ejemplo, en orden creciente por número de formas superpuestas, también podría ayudar .

Ilmari Karonen
fuente
Sí, creo que podría usar un ILP para resolverlo relativamente sin dolor debido al tamaño del problema. 2 ^ 20 ~ = 1,000,000 así que dado que solo puede haber tantas formas de T, debería estar bien usando un solucionador lineal para esto . Es claramente exponencialmente complejo (al menos hasta que alguien logre demostrar que p = np). El tamaño permite evitar la heurística en este caso relativamente simple.
AturSams
Ilmari, muchas gracias. Esta respuesta también tomará algunos pasos para entender. El bit de formas arbitrarias bien puede ser útil en futuras iteraciones.
Ensamblador