Imagine un mundo basado en cubos (como Minecraft, Trove o Cube World) donde todo está formado por cubos de tamaño idéntico y todos los cubos son del mismo tipo .
El objetivo es representar al mundo con el menor número de cajas rectangulares (fusionando cubos pero conservando la forma convexa (también conocida como forma de caja rectangular)). Mi algoritmo tiene éxito en esto, pero su rendimiento es demasiado lento para el propósito previsto. ¿Hay algoritmos más rápidos?
El código pseudo-C # para mi algoritmo es básicamente:
struct Coordinate { int x,y,z; }; //<-- integer based grid
HashSet<Coordinate> world; // <-- contains all the cubes
//width, height, and length represent how many cubes it spans
struct RectangularBox { Coordinate coord; int width,height,length; }
void Begin()
{
List<RectangularBox> fewestBoxes = new List<RectangularBox>();
while(world.Count > 0)
{
RectangularBox currentLargest = ExtractLargest();
fewestBoxes.Add(currentLargest);
world.RemoveRange(currentLargest.ContainedCubes());
}
//done; `fewestBoxes` contains the fewest rectangular boxes needed.
}
private RectangularBox ExtractLargest()
{
RectangularBox largestBox = new RectangularBox();
foreach (Coordinate origin in world)
{
RectangularBox box = FindMaximumSpan(origin);
if (box.CalculateVolume() >= largestBox.CalculateVolume())
largestBox = box;
}
return largestBox;
}
private RectangularBox FindMaximumSpan(Coordinate origin)
{
int maxX, maxY,maxZ;
while (world.Contains(origin.Offset(maxX, 0, 0))) maxX++;
while (world.Contains(origin.Offset(0, maxY, 0))) maxY++;
while (world.Contains(origin.Offset(0, 0, maxZ))) maxZ++;
RectangularBox largestBox;
for (int extentX = 0; extentX <= maxX; extentX++)
for (int extentY = 0; extentY <= maxY; extentY++)
for (int extentZ = 0; extentZ <= maxZ; extentZ++)
{
int lengthX = extentX + 1;
int lengthY = extentY + 1;
int lengthZ = extentZ + 1;
if (BoxIsFilledWithCubes(origin, lengthX, lengthY, lengthZ))
{
int totalVolume = lengthX * lengthY * lengthZ;
if (totalVolume >= largestBox.ComputeVolume())
largestBox = new RectangularBox(origin, lengthX, lengthY, lengthZ);
}
else
break;
}
return largestBox;
}
private bool BoxIsFilledWithCubes(Coordinate coord,
int lengthX, int lengthY, int lengthZ)
{
for (int gX = 0; gX < lengthX; gX++)
for (int gY = 0; gY < lengthY; gY++)
for (int gZ = 0; gZ < lengthZ; gZ++)
if (!world.Contains(coord.Offset(gX, gY, gZ)))
return false;
return true;
}
Esencialmente, para cada bloque en el mundo, primero encuentra cuántos bloques contiguos hay en cada una de las tres dimensiones positivas (+ X, + Y, + Z). Y luego inunda un poco ese volumen y comprueba cuál es el relleno más grande al que no le faltan bloques.
Actualización: como parecía haber dado a entender que esto era para el motor de renderizado de un juego, solo quiero aclarar que no es para el motor de renderizado de un juego; es para un convertidor de archivos; sin GUI
fuente
Respuestas:
Puedes aprovechar el hecho de que cuando
devuelve verdadero, entonces no es necesario verificar
BoxIsFilledWithCubes(c,x+1,y,z)
todos esos cubos en el rango de coordenadas "(c, x, y, z)" nuevamente. Solo necesita verificar esos cubos con la nueva coordenada xc.x + (x+1)
. (Lo mismo es cierto paray+1
, oz+1
). En términos más generales, al dividir una caja en dos cajas más pequeñas (para las cuales quizás ya sepa si ambas están llenas de cubos, o no ambas), puede aplicar aquí una técnica de dividir y conquistar, que se vuelve más rápida que su enfoque original cuando almacena en caché los resultados intermedios.Para lograr esto, comienza a implementar de forma
BoxIsFilledWithCubes(c,x,y,z)
recursiva, por ejemplo:y luego use la memorización (como se discute aquí ) para evitar cualquier llamada repetida
BoxIsFilledWithCubes
con los mismos parámetros. Tenga en cuenta que tendrá que borrar el caché de la memoria cuando aplique un cambio a suworld
contenedor, como porworld.RemoveRange
. Sin embargo, supongo que esto hará que su programa sea más rápido.fuente
Construya un octree con un nodo de hoja de tamaño aabb del tamaño de su caja. Mientras recorres el octree puedes fusionar nodos de forma económica. Los nodos completamente llenos son triviales para fusionarse (nuevo cuadro = aabb padre), mientras que para nodos parcialmente llenos, puede usar una variante de su algoritmo actual para verificar la capacidad de fusión.
fuente
Parece que tiene al menos O (n ^ 2) (vea la notación O grande ) mientras recorre todos los cuadros del mundo en "Begin ()", luego, para cada cuadro, recorre todos los cuadros del mundo en ExtractLargest ( ) Entonces, un mundo con 10 cajas no relacionadas tomará 4 veces más tiempo que un mundo con 5 cajas no relacionadas.
Por lo tanto, debe limitar el número de cuadros que ExtractLargest () tiene que mirar, para hacerlo necesita usar algún tipo de búsqueda espacial , ya que está trabajando en 3d, es posible que necesite una búsqueda espacial 3d. Sin embargo, primero comience por comprender la búsqueda espacial 2D.
Luego, considere si alguna vez tendrá muchos cuadros uno encima del otro, si no, entonces una búsqueda espacial en 2D que solo cubre x, y puede ser suficiente para reducir su bucle.
Octree / quadtree son una opción, pero hay muchas otras opciones para particionar el espacio ,
Pero es posible que pueda usar una matriz bidimensional de listas ( índice espacial de cuadrícula ), donde todos los cuadros que cubren el punto (a, b) están en la matriz [a, b] .list. Pero lo más probable es que eso conduzca a una matriz demasiado grande, entonces, ¿qué pasa con la lista array [mod (a, 100), mob (b, 100)]? Todo esto depende de cómo son sus datos .
(He visto que la solución de red funciona muy bien en la vida real).
O haga lo que Wilbert dice con un octree con un nodo de hoja de un tamaño del tamaño de su caja, pero más tarde es probable que tenga que encontrar la caja a la que apunta el mouse del usuario, etc., una vez más, un caso de búsqueda espacial.
( Debe decidir si solo está tratando de hacer que este software funcione, o si está tratando de comprender cómo ser un mejor programador y, por lo tanto, está más interesado en el aprendizaje que en una solución rápida ) .
fuente