在递归调用中更改列表,同时将列表的克隆作为参数传递 (C#)
List being altered in recursive calls while passing clones of the list as argument (C#)
我正在创建一种算法来使用约束传播和局部搜索(类似于 Norvig 的)来解决数独问题。为此,我跟踪数独中每个方块的可能值列表。对于每次尝试为正方形分配新值的尝试,我都会克隆数组并将其递归地传递给算法的搜索方法。然而,不知何故这个列表仍在被改变。它涉及的方法是:
List<int>[,] search(List<int>[,] vals)
{
if (vals == null)
return null;
if (isSolved(vals))
return vals;
//get square with lowest amt of possible values
int min = Int32.MaxValue;
Point minP = new Point();
for (int y = 0; y < n * n; y++)
{
for (int x = 0; x < n * n; x++)
{
if (vals[y, x].Count < min && vals[y, x].Count > 1)
{
min = vals[y, x].Count;
minP = new Point(x, y);
}
}
}
for(int i=vals[minP.Y, minP.X].Count-1; i >= 0; i--)
{
//<Here> The list vals[minP.Y, minP.X] is altered within this for loop somehow, even though the only thing done with it is it being cloned and passed to the assign method for another (altered) search afterwards
Console.WriteLine(vals[minP.Y, minP.X].Count + "-" + min);
int v = vals[minP.Y, minP.X][i];
List<int>[,] newVals = (List<int>[,])vals.Clone();
List<int>[,] l = search(assign(minP, v, newVals));
if (l != null)
return l;
}
return null;
}
列表 vals[minP.Y, minP.X] 在 for 循环中以某种方式被改变,导致它最终尝试将正方形传递给具有 1(或最终甚至为 0)的 assign 方法可能的值。 Console.Writeline 语句显示 vals[minP.Y, minP.X].Count 最终将不同于 'min' 变量(在 for 循环上面定义相同)。
如果有人可以帮助我了解如何在此 for 循环中更改列表以及如何修复它,我们将不胜感激!
此致。
编辑:编辑这些列表的方法(但是在克隆版本中):
List<int>[,] assign(Point p, int v, List<int>[,] vals)
{
int y = p.Y, x = p.X;
for (int i = vals[y, x].Count - 1; i >= 0; i--)
{
int v_ = vals[y, x][i];
if (v_ != v && !eliminate(p, v_, vals))
return null;
}
return vals;
}
bool eliminate(Point p, int v, List<int>[,] vals)
{
if (!vals[p.Y, p.X].Remove(v))
return true; //al ge-elimineerd
// Update peers when only 1 possible value left
if (vals[p.Y, p.X].Count == 1)
foreach (Point peer in peers[p.Y, p.X])
if(!eliminate(peer, vals[p.Y, p.X][0], vals))
return false;
else if (vals[p.Y, p.X].Count == 0)
return false;
// Update units
List<Point> vplaces = new List<Point>();
foreach (Point unit in units[p.Y, p.X])
{
if (vals[unit.Y, unit.X].Contains(v))
{
vplaces.Add(unit);
if (vplaces.Count > 1)
continue;
}
}
if (vplaces.Count == 0)
return false;
else if (vplaces.Count == 1)
{
Console.WriteLine("test");
if (assign(vplaces[0], v, vals) == null)
return false;
}
return true;
}
你的问题在于
List<int>[,] newVals = (List<int>[,])vals.Clone();
Array.Clone()
并没有按照您认为的那样进行。 List<int>[,]
表示 List<int>
个对象的二维数组 - 实际上是一个三维数组。由于 List<int>
不是基本值类型,.Clone()
创建数组的 浅拷贝 。
换句话说,它创建了一个全新的二维数组,其中每个值都有一个指向与旧数组相同的 List<int>
的指针。如果 C# 让您直接操作指针,您可以开始更改它们,但由于它不允许,任何时候您访问底层 List<int>
,无论它是否在 [=18= 之前,您都会得到相同的指针] 或之后。
查看文档here, and some solutions are here and here。
实际上,您需要重写该行,而不是复制数组本身,而是将所有 值 复制到新的 List<int>
中。
我正在创建一种算法来使用约束传播和局部搜索(类似于 Norvig 的)来解决数独问题。为此,我跟踪数独中每个方块的可能值列表。对于每次尝试为正方形分配新值的尝试,我都会克隆数组并将其递归地传递给算法的搜索方法。然而,不知何故这个列表仍在被改变。它涉及的方法是:
List<int>[,] search(List<int>[,] vals)
{
if (vals == null)
return null;
if (isSolved(vals))
return vals;
//get square with lowest amt of possible values
int min = Int32.MaxValue;
Point minP = new Point();
for (int y = 0; y < n * n; y++)
{
for (int x = 0; x < n * n; x++)
{
if (vals[y, x].Count < min && vals[y, x].Count > 1)
{
min = vals[y, x].Count;
minP = new Point(x, y);
}
}
}
for(int i=vals[minP.Y, minP.X].Count-1; i >= 0; i--)
{
//<Here> The list vals[minP.Y, minP.X] is altered within this for loop somehow, even though the only thing done with it is it being cloned and passed to the assign method for another (altered) search afterwards
Console.WriteLine(vals[minP.Y, minP.X].Count + "-" + min);
int v = vals[minP.Y, minP.X][i];
List<int>[,] newVals = (List<int>[,])vals.Clone();
List<int>[,] l = search(assign(minP, v, newVals));
if (l != null)
return l;
}
return null;
}
列表 vals[minP.Y, minP.X] 在 for 循环中以某种方式被改变,导致它最终尝试将正方形传递给具有 1(或最终甚至为 0)的 assign 方法可能的值。 Console.Writeline 语句显示 vals[minP.Y, minP.X].Count 最终将不同于 'min' 变量(在 for 循环上面定义相同)。
如果有人可以帮助我了解如何在此 for 循环中更改列表以及如何修复它,我们将不胜感激!
此致。
编辑:编辑这些列表的方法(但是在克隆版本中):
List<int>[,] assign(Point p, int v, List<int>[,] vals)
{
int y = p.Y, x = p.X;
for (int i = vals[y, x].Count - 1; i >= 0; i--)
{
int v_ = vals[y, x][i];
if (v_ != v && !eliminate(p, v_, vals))
return null;
}
return vals;
}
bool eliminate(Point p, int v, List<int>[,] vals)
{
if (!vals[p.Y, p.X].Remove(v))
return true; //al ge-elimineerd
// Update peers when only 1 possible value left
if (vals[p.Y, p.X].Count == 1)
foreach (Point peer in peers[p.Y, p.X])
if(!eliminate(peer, vals[p.Y, p.X][0], vals))
return false;
else if (vals[p.Y, p.X].Count == 0)
return false;
// Update units
List<Point> vplaces = new List<Point>();
foreach (Point unit in units[p.Y, p.X])
{
if (vals[unit.Y, unit.X].Contains(v))
{
vplaces.Add(unit);
if (vplaces.Count > 1)
continue;
}
}
if (vplaces.Count == 0)
return false;
else if (vplaces.Count == 1)
{
Console.WriteLine("test");
if (assign(vplaces[0], v, vals) == null)
return false;
}
return true;
}
你的问题在于
List<int>[,] newVals = (List<int>[,])vals.Clone();
Array.Clone()
并没有按照您认为的那样进行。 List<int>[,]
表示 List<int>
个对象的二维数组 - 实际上是一个三维数组。由于 List<int>
不是基本值类型,.Clone()
创建数组的 浅拷贝 。
换句话说,它创建了一个全新的二维数组,其中每个值都有一个指向与旧数组相同的 List<int>
的指针。如果 C# 让您直接操作指针,您可以开始更改它们,但由于它不允许,任何时候您访问底层 List<int>
,无论它是否在 [=18= 之前,您都会得到相同的指针] 或之后。
查看文档here, and some solutions are here and here。
实际上,您需要重写该行,而不是复制数组本身,而是将所有 值 复制到新的 List<int>
中。