【发布时间】:2014-02-27 03:14:50
【问题描述】:
我需要找到一个有效的算法来做到这一点:
byte[,] initialArray
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 5 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 5 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
到这里:
byte[,] resultArray
0 0 0 1 2 1 0 0 0 0
0 0 1 2 3 2 1 0 0 0
0 1 2 3 4 3 2 1 0 0
1 2 3 4 5 4 3 2 1 0
0 1 2 3 4 3 2 1 0 0
0 1 2 2 3 2 1 0 0 0
1 2 3 2 2 1 0 0 0 0
2 3 4 3 2 1 0 0 0 0
3 4 5 4 3 2 1 0 0 0
2 3 4 3 2 1 0 0 0 0
发生了什么
初始数组有两个设置为初始值的单元格,其他单元格设置为 0。算法需要将该值“传播”到相邻单元格(没有对角线,只是上/下/左/右) .每次该值传播到一个新单元格时,该值减 1 并再次递归传播。当值达到 0 时停止。
如果值扩散到值 > 0 的单元格,则应保留两个值中的最大值,而不是简单地覆盖。
该示例显示的是 2D 数组,但我实际上使用的是 3D 数组。
我的尝试
我已经设法在 C# 中创建了一个简单的递归算法。它有效,但它必须非常低效。这是在具有 4 个初始单元格且值 >= 10 的大型 3D 阵列上放慢速度的方法。必须有更好的方法来做到这一点(对于那些熟悉的人,这是 Minecraft 游戏用来确定每个单元格的光强度的方法游戏中的单元格。Minecraft 级别的阵列非常庞大,可以包含许多光源)
我正在寻找最有效的方法。这是我对 3D 数组的 C# 实现:
main ... {
List<int[]> toCheck = new List<int[]>();
// This list will keep a record of the initial cells that have a value > 0
// For loop over each cell to find those with initial value > 0
for (int x=0; x<worldX; x++){
for (int y=0; y<worldY; y++){
for (int z=0; z<worldZ; z++){
if (data[x,y,z].light > 0)
toCheck.Add (new int[] {x,y,z});
}
}
}
// For each cell w/ initial value > 0, spread the light
foreach (int[] i in toCheck)
SpreadLight(i[0],i[1],i[2],(byte)(data[i[0],i[1],i[2]].light - 1));
}
void SpreadLight(int x, int y, int z, byte light) {
try {
// Make sure this cells current value is smaller than the value we want to assign to it
if (data[x,y,z].light < light) {
data[x,y,z].light = (byte)light;
}
// If the value at this cell is > 0, get adjacent cells and spread the light to each of them
if (light > 0) {
int[][] adjBlocks = GetAdjacentBlocks(x,y,z);
for (int i = 0; i < 6; i++) {
SpreadLight(adjBlocks[i][0], adjBlocks[i][1], adjBlocks[i][2],(byte)(light-1));
}
}
}
catch { return; } // I'm not proud if this, it's the easiest way I found to avoid out of array bounds error
}
// This method simply returns an array with the 3D coordinates of each adjacent cell
int[][] GetAdjacentBlocks(int x, int y, int z) {
int[][] result = new int[6][];
// Top
result[0] = new int[] {x, y+1, z};
// North
result[1] = new int[] {x, y, z+1};
// East
result[2] = new int[] {x+1, y, z};
// South
result[3] = new int[] {x, y, z-1};
// West
result[4] = new int[] {x-1, y, z};
// Bottom
result[5] = new int[] {x, y-1, z};
return result;
}
【问题讨论】:
-
使用
catch方法以避免数组越界错误,您的代码会慢很多。抛出和捕获异常需要大量开销... -
如果 Minecraft 真的使用了这种算法,我会感到惊讶。更有可能的是,当它绘制一个对象时,它会获取范围内所有光源的列表,然后计算该点上每个光源的光强度。在没有要绘制的物体的情况下,无需计算光的强度。
-
我不会使用二维数组(标题误导恕我直言)。我会使用带有
index = N*row+column的一维数组。先解决 2D 问题,再询问是否带入 3D。 -
我可以想到一个使用有限差分的
N*M×N*M稀疏矩阵的解决方案,但编码将是一项艰巨的任务。 -
@ja72:我的思绪也沿着矩阵大道走——但是,正如你所说,编码并不完全是野餐! :)