我需要找到一个有效的算法:
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时,它会停止。
如果值正在扩展到值> gt的单元格0,应保留两个值中最大的一个,而不是简单地覆盖。
该示例显示了一个2D数组,但我实际上正在使用3D数组。
我的尝试
我已经设法在C#中创建一个简单的递归算法。它有效,但必须非常低效。这是一个减慢大型3D阵列的方法,其中4个初始单元的值> = 10.必须有一个更好的方法来做到这一点(对于那些熟悉的人来说,这是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;
}
答案 0 :(得分:3)
试试这个。根据我的经验,1D阵列的工作速度比2D阵列快得多。还实现了几个计算快捷方式。
class Program
{
static void Main(string[] args)
{
Area A=new Area(10, 10);
A[3, 4]=5;
A[8, 2]=5;
Console.WriteLine(A);
//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
bool spread1=A.CheckSpread();
Console.WriteLine();
Console.WriteLine("Spreading...");
A.Spread();
bool spread2=A.CheckSpread();
Console.WriteLine(A);
//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
}
}
public struct Area
{
byte[] map;
int rows, columns;
public Area(int rows, int columns)
{
this.map=new byte[rows*columns];
this.columns=columns;
this.rows=rows;
}
public Area(Area other)
: this(other.rows, other.columns)
{
Array.Copy(other.map, this.map, other.map.Length);
}
public Area(byte[,] array)
{
this.rows=array.GetLength(0);
this.columns=array.GetLength(1);
this.map=new byte[rows*columns];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
this.map[i*columns+j]=array[i, j];
}
}
}
public int Rows { get { return rows; } }
public int Columns { get { return columns; } }
public byte[] Map { get { return map; } }
public byte this[int index]
{
get { return map[index]; }
set { map[index]=value; }
}
public byte this[int row, int column]
{
get { return map[row*columns+column]; }
set { map[row*columns+column]=value; }
}
public byte[,] ToArray2()
{
byte[,] array=new byte[rows, columns];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
array[i, j]=map[i*columns+j];
}
}
return array;
}
public void Spread()
{
bool changed;
do // CAUTION: This is not guaranteed to exit. Or is it?
{
changed=false;
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int i=k/columns;
int j=k%columns;
int k_N=i>0?(i-1)*columns+j:-1;
int k_S=i<rows-1?(i+1)*columns+j:-1;
int k_E=j<columns-1?i*columns+j+1:-1;
int k_W=j>0?i*columns+j-1:-1;
if (k_N>=0&&map[k_N]+1<x) { map[k_N]=(byte)(x-1); changed=true; }
if (k_S>=0&&map[k_S]+1<x) { map[k_S]=(byte)(x-1); changed=true; }
if (k_E>=0&&map[k_E]+1<x) { map[k_E]=(byte)(x-1); changed=true; }
if (k_W>=0&&map[k_W]+1<x) { map[k_W]=(byte)(x-1); changed=true; }
}
} while (changed);
}
public bool CheckSpread()
{
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int i=k/columns;
int j=k%columns;
int k_N=i>0?(i-1)*columns+j:-1;
int k_S=i<rows-1?(i+1)*columns+j:-1;
int k_E=j<columns-1?i*columns+j+1:-1;
int k_W=j>0?i*columns+j-1:-1;
if (k_N>=0&&map[k_N]+1<x) return false;
if (k_S>=0&&map[k_S]+1<x) return false;
if (k_E>=0&&map[k_E]+1<x) return false;
if (k_W>=0&&map[k_W]+1<x) return false;
}
return true;
}
public override string ToString()
{
string[] table=new string[rows];
for (int i=0; i<rows; i++)
{
string[] row=new string[columns];
for (int j=0; j<columns; j++)
{
row[j]= string.Format("{0,-3}", map[i*columns+j]);
}
table[i]= string.Join(" ", row);
}
return string.Join(Environment.NewLine, table);
}
}
public struct Area3
{
byte[] map;
int rows, columns, pages;
public Area3(int rows, int columns, int pages)
{
this.map=new byte[rows*columns*pages];
this.columns=columns;
this.rows=rows;
this.pages=pages;
}
public Area3(Area3 other)
: this(other.rows, other.columns, other.pages)
{
Array.Copy(other.map, this.map, other.map.Length);
}
public Area3(byte[, ,] array)
{
this.rows=array.GetLength(0);
this.columns=array.GetLength(1);
this.pages=array.GetLength(2);
this.map=new byte[rows*columns*pages];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
for (int l=0; l<pages; l++)
{
this.map[(l*rows+i)*columns+j]=array[i, j, l];
}
}
}
}
public int Rows { get { return rows; } }
public int Columns { get { return columns; } }
public int Pages { get { return pages; } }
public byte[] Map { get { return map; } }
public byte this[int index]
{
get { return map[index]; }
set { map[index]=value; }
}
public byte this[int row, int column, int page]
{
get { return map[(page*rows+row)*columns+column]; }
set { map[(page*rows+row)*columns+column]=value; }
}
public byte[, ,] ToArray3()
{
byte[, ,] array=new byte[rows, columns, pages];
for (int i=0; i<rows; i++)
{
for (int j=0; j<columns; j++)
{
for (int l=0; l<pages; l++)
{
array[i, j, l]=map[(l*rows+i)*columns+j];
}
}
}
return array;
}
public void Spread()
{
bool changed;
do
{
changed=false;
for (int k=0; k<map.Length; k++)
{
byte x=map[k];
if (x<=1) continue; // cannot affect neighbors
int l=k/(rows*columns);
int i=(k%(rows*columns))/columns;
int j=(k%(rows*columns))%columns;
int k_N=i>0?(l*rows+i-1)*columns+j:-1;
int k_S=i<rows-1?(l*rows+i+1)*columns+j:-1;
int k_E=j<columns-1?(l*rows+i)*columns+j+1:-1;
int k_W=j>0?(l*rows+i)*columns+j-1:-1;
int k_U=l<pages-1?((l+1)*rows+i)*columns+j:-1;
int k_D=l>0?((l-1)*rows+i)*columns+j:-1;
if (k_N>=0&&map[k_N]+1<x) { map[k_N]=(byte)(x-1); changed=true; }
if (k_S>=0&&map[k_S]+1<x) { map[k_S]=(byte)(x-1); changed=true; }
if (k_E>=0&&map[k_E]+1<x) { map[k_E]=(byte)(x-1); changed=true; }
if (k_W>=0&&map[k_W]+1<x) { map[k_W]=(byte)(x-1); changed=true; }
if (k_U>=0&&map[k_U]+1<x) { map[k_U]=(byte)(x-1); changed=true; }
if (k_D>=0&&map[k_D]+1<x) { map[k_D]=(byte)(x-1); changed=true; }
}
} while (changed);
}
}
没有可视化编码为3D。
答案 1 :(得分:2)
以下实现(2D)应该更快 - 它将对象创建和不必要的函数调用保持在最低限度。扩展到3D将非常简单:
class Program
{
class ArrayPoint { public int x; public int y;}
private static byte[,] startArray =
{
{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}
};
private static int rows = startArray.GetLength(0);
private static int cols = startArray.GetLength(1);
static void Main(string[] args)
{
Spread(startArray);
}
static void Spread(byte[,] array)
{
var points = GetStartPoints(array);
foreach (var point in points.ToList())
SpreadPoint(array, point.x, point.y);
Display(array);
}
static void SpreadPoint(byte[,] array, int x, int y)
{
for (var i = x-1; i < x+2; i++)
for (var j = y-1; j < y+2; j++)
if ( (i==x || j==y) && !(i==x && j==y) && (i >= 0 && i < rows && j >= 0 && j < cols)
&& array[i, j] + 1 < array[x, y])
{
array[i, j] = (byte)(array[x, y] - 1);
SpreadPoint(array, i, j);
}
}
static void Display(byte[,] array)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
Console.Write("{0} ",array[i,j]);
Console.WriteLine();
}
Console.WriteLine();
}
static IEnumerable<ArrayPoint> GetStartPoints(byte[,] array)
{
for (var i = 0; i < rows; i++)
for (var j = 0; j < cols; j++)
if (array[i, j] != 0)
yield return new ArrayPoint {x = i, y = j};
}
}
输出是:
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
答案 2 :(得分:0)
我的两分钱:考虑在波前同时扩大积分。因此,当比其他人更早地完成时,可以终止个别波前。
一个简化的JavaScript示例:
var s = new Array(81)
for (var i=0; i<81; i++){
s[i] = i == 40 ? 5 : 0
}
var max = 5, m = 9, n = 9
function expand(wavefront){
while (wavefront.val > 0) {
//Southwest
var tmpLoc = wavefront.loc--
for (var i=0; i<max - 1 - wavefront.val; i++){
s[tmpLoc] = wavefront.val
tmpLoc += n + 1
}
//Southeast...
//...
wavefront.val--
}
}
expand({loc: 49, val: 4})
s
现在看起来像这样:
[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,1,2,3,0,0,0,0,0
,0,0,1,2,0,0,0,0,0
,0,0,0,1,0,0,0,0,0
,0,0,0,0,0,0,0,0,0]