C# 如何创建一个半随机的数字网格,使每个数字只出现一次,最重要的是不太接近其前序数字?
假设我有一个网格(10x10),我想用数字序列0-99填充它。以下是一些条件:C# 如何创建一个半随机的数字网格,使每个数字只出现一次,最重要的是不太接近其前序数字?,c#,algorithm,math,random,grid,C#,Algorithm,Math,Random,Grid,假设我有一个网格(10x10),我想用数字序列0-99填充它。以下是一些条件: 序列中的每个数字在网格中只能出现一次 每个数字(从序列中)的位置应该是随机的(x,y坐标) 序列中的后续数字(例如5和6)不能出现在彼此相邻(或半径范围内)的网格中 我可以处理条件1和2,没问题。条件3(对我来说)更难。我使用蛮力填充网格,但这有几个问题。首先,当网格较大(100x100)时,速度较慢。其次(也是最重要的),暴力并不保证解决方案(例如,序列中的最后两个数字将彼此相邻,因此不允许->无解决方案) 如果有
NxN
网格,生成一个数字列表0..NxN-1
,然后将其洗牌(Fisher-Yates或其他)
然后,填写网格:对于每个单元格,在无序列表中选择第一个元素,该元素将根据您的规则和网格的当前状态进行调整。将每个元素移动到网格时,将其从无序列表中移除
如果由于无序列表中没有剩余的有效数字而无法填充网格单元格,请在填充的网格单元格中搜索,直到找到一个单元格,该单元格中的数字在此处有效,而无序列表中剩余的一个数字在此处也有效。然后移动到此处,并将有效号码从列表中移动到此处
我不确定你是否可能找不到这样一个数字,除非你的半径设置得太高,任何解决方案都不存在。读者练习;) 这听起来像是我用来为游戏生成星域的算法。我在笛卡尔平面上布置了一个给定维度的星系和所需数量的恒星。恒星不可能占据同一个位置,也不可能以n个单位相互存在。我偷了一些东西,但这应该有用
for (int i = 0; i < u.StarCount; i++)
{
bool badStar = true; //Assume failure
do
{
//Create a star, get a random location, and where the rarity of its spectral type
StellarBody sb = new StellarBody();
sb.PositionX = r.Next(0, u.width);
sb.PositionY = r.Next(0, u.height);
int randomAbundance = r.Next(maxAbundance);
//Test the location against previous stars added, disallow positions where the centers are within 8 units of one another
if (!u.StellarBodies.Any(p => Math.Abs(p.PositionX.Value - sb.PositionX.Value) < minGap && Math.Abs(p.PositionY.Value - sb.PositionY.Value) < minGap))
{
//Get the spectral types based on the abundance value of the spectral types compared to the random abundance number
List<Models.StellarClass> abundanceTypes = starTypes.FindAll(f => f.Abundance == starTypes.Where(p => p.Abundance > randomAbundance).Min(m => m.Abundance));
try
{
int index = r.Next(0, abundanceTypes.Count());
sb.StellarClassID = abundanceTypes[index].StellarClassID;
sb.CatalogDesignation = index.ToString() + u.StellarBodies.Count.ToString()
+ abundanceTypes[index].Code + "-" + CoordinateMath.GetMortonNumber((int)sb.PositionX, (int)sb.PositionY).ToString();
minOrbit = abundanceTypes[index].MinOrbitZone;
maxOrbit = abundanceTypes[index].MaxOrbitZone;
}
catch (Exception ex)
{
sb.StellarClassID = starTypes.First(p => p.Code.Equals("Dork", StringComparison.CurrentCultureIgnoreCase)).StellarClassID;
}
u.StellarBodies.Add(sb);
badStar = false;
}
} while (badStar);
}
for(int i=0;iMath.Abs(p.PositionX.Value-sb.PositionX.Value)f.funsity==starTypes.Where(p=>p.funsity>randomFunsity).Min(m=>m.funsity));
尝试
{
int index=r.Next(0,abundantypes.Count());
sb.StellarClassID=丰度类型[索引].StellarClassID;
sb.catalogsignation=index.ToString()+u.StellarBodies.Count.ToString()
+丰富类型[索引]。代码+“-”+坐标Math.GetMortonNumber((int)sb.PositionX,(int)sb.PositionY).ToString();
米诺比=丰度类型[索引]。米诺比酮;
maxOrbit=丰度类型[索引].MaxOrbitZone;
}
捕获(例外情况除外)
{
sb.StellarClassID=starTypes.First(p=>p.Code.Equals(“Dork”,StringComparison.CurrentCultureIgnoreCase)).StellarClassID;
}
u、 加上(某人);
坏星=假;
}
}while(badStar);
}
您可以使用递归回溯算法,在每个单元格中放入一个随机有效数字。如果没有有效的单元格,它将回溯并为上一个单元格拾取另一个数字。使用枚举器方法,可以轻松构建回溯系统
class Generator
{
public int Width { get; private set; }
public int Height { get; private set; }
public int Radius { get; private set; }
private List<int> _numbers;
private bool[] _picked;
private int[] _grid;
private Random _rnd;
public Generator(int width, int height, int radius)
{
Width = width;
Height = height;
Radius = radius;
_rnd = new Random();
_numbers = Enumerable.Range(0,Width*Height).OrderBy(_ => _rnd.Next()).ToList();
_picked = _numbers.Select(n => false).ToArray();
_grid = new int[width*height];
}
public int[] Generate()
{
return Generate(0)
.Select(a => a.ToArray()) // copy
.FirstOrDefault();
}
private IEnumerable<int[]> Generate(int index)
{
if (index >= Width * Height)
{
yield return _grid;
yield break;
}
int xmid = index%Width;
int xlow = Math.Max(0, xmid - Radius);
int xhigh = Math.Min(xmid + Radius, Width - 1);
int ymid = index/Width;
int ylow = Math.Max(0, ymid - Radius);
int yhigh = ymid;
var validNumbers = _numbers
.Where(n =>
!_picked[n] &&
Enumerable.Range(xlow, xhigh - xlow + 1).All(x =>
Enumerable.Range(ylow, yhigh-ylow+1).All(y =>
y*Width + x >= index // Not generated yet
|| Math.Abs(x - xmid) + Math.Abs(y - ymid) > Radius // Outside radius
|| Math.Abs(_grid[y*Width+x] - n) > 1 // Out of range
)
)
)
.ToList();
foreach (var n in validNumbers)
{
_grid[index] = n;
_picked[n] = true;
foreach (var sol in Generate(index + 1))
{
yield return sol;
}
_picked[n] = false;
}
}
}
它通常很快,在一秒钟内完成。有时,它在开始时做出了错误的选择,并且必须进行多次回溯。我会快速填充网格(尽量避免结块,但不要太用力)。当网格被填充时,我会找到聚集的数字并将它们四处移动。不涉及回溯。这种方法对于非常大的网格应该非常有效 第一阶段的伪代码:
int N = 100;
int minDistance = 10;
int maxCollisionCount = 5;
int saturationThreshold = N * N * 0.85;
grid[0,0] = 1;
int oldX = 0, oldY = 0;
int newX, newY;
for (i = 2; i <= N * N; i++)
{
bool foundNewCell = false;
for (collisionCount = 0; collisionCount < maxCollisionCount; collisionCount++)
{
newX = rnd(0, N - minDistance);
if (newX >= oldX - minDistance / 2)
newX += minDistance;
newY = rnd(0, N - minDistance);
if (newY >= oldY - minDistance / 2)
newY += minDistance;
if (grid[newX, newY] == 0)
{
grid[oldX = newX, oldY = newY] = i;
foundNewCell = true;
break;
}
if (i > saturationThreshold)
break;
}
if (foundNewCell)
continue;
// Find newX and newY by some other way, even if there would be clumping.
// For instance use already randomed newX and newY and circle around until
// you find an empty cell
...
grid[oldX = newX, oldY = newY] = i;
}
int N=100;
智力距离=10;
int maxCollisionCount=5;
int饱和阈值=N*N*0.85;
网格[0,0]=1;
int oldX=0,oldY=0;
int newX,newY;
对于(i=2;i=oldX-minDistance/2)
newX+=思维距离;
newY=rnd(0,N-思维距离);
if(newY>=oldY-minDistance/2)
新奇+=思维距离;
if(网格[newX,newY]==0)
{
网格[oldX=newX]
int N = 100;
int minDistance = 10;
int maxCollisionCount = 5;
int saturationThreshold = N * N * 0.85;
grid[0,0] = 1;
int oldX = 0, oldY = 0;
int newX, newY;
for (i = 2; i <= N * N; i++)
{
bool foundNewCell = false;
for (collisionCount = 0; collisionCount < maxCollisionCount; collisionCount++)
{
newX = rnd(0, N - minDistance);
if (newX >= oldX - minDistance / 2)
newX += minDistance;
newY = rnd(0, N - minDistance);
if (newY >= oldY - minDistance / 2)
newY += minDistance;
if (grid[newX, newY] == 0)
{
grid[oldX = newX, oldY = newY] = i;
foundNewCell = true;
break;
}
if (i > saturationThreshold)
break;
}
if (foundNewCell)
continue;
// Find newX and newY by some other way, even if there would be clumping.
// For instance use already randomed newX and newY and circle around until
// you find an empty cell
...
grid[oldX = newX, oldY = newY] = i;
}