将数组改组到与原始数组匹配的位置后,为什么将它们等同 return 为假?
After shuffling an array to the point where it matches the original, why does equating them return false?
考虑以下洗牌方法,给定一个对象数组a
- 从
a
中取出第一个元素并将其放入b
。考虑 b
中此元素的索引为 x
.
- 将
a
中的第二个元素放在 b[x]
的前面,这样它现在就在 b[x-1]
的位置
- 将
a
中的第三个元素放在 b[x]
后面,这样它现在就在 b[x+1]
的位置
- 将
a
中的第四个元素放在b[x - 1]的前面,这样它现在就在b[x-2]
的位置
- 将
a
中的第 5 个元素放在 b[x+1]
后面,使其现在位于 b[x+2]
的位置
- 重复此过程,直到
b
以新的随机顺序包含 a
中的所有元素。
我写了一些代码来执行此操作,如下所示。它会在上面的过程中不断对数组进行shuffle,直到shuffle后的数组与原数组匹配,然后return shuffle的次数
public class BadShuffler
{
public BadShuffler(object[] _arrayToShuffle)
{
originalArray = _arrayToShuffle;
Arrays = new List<object[]>
{
originalArray
};
}
private object[] originalArray;
private int count;
public List<object[]> Arrays { get; set; }
public int Shuffle(object[] array = null)
{
if (array == null)
array = originalArray;
count++;
object[] newArray = new object[array.Length];
bool insertAtEnd = false;
int midpoint = newArray.Length / 2;
newArray[midpoint] = array[0];
int newArrayInteger = 1;
int originalArrayInteger = 1;
while (newArray.Any(x => x == null))
{
if (insertAtEnd)
{
newArray[midpoint + newArrayInteger] = array[originalArrayInteger];
newArrayInteger++;
}
else
{
newArray[midpoint - newArrayInteger] = array[originalArrayInteger];
}
originalArrayInteger++;
insertAtEnd = !insertAtEnd;
}
Arrays.Add(newArray);
return (newArray.All(x => x == originalArray[Array.IndexOf(newArray, x)])) ? count : Shuffle(newArray);
}
}
虽然不是世界上最漂亮的东西,但它确实可以。示例如下:
Shuffled 6 times.
1, 2, 3, 4, 5, 6
6, 4, 2, 1, 3, 5
5, 1, 4, 6, 2, 3
3, 6, 1, 5, 4, 2
2, 5, 6, 3, 1, 4
4, 3, 5, 2, 6, 1
1, 2, 3, 4, 5, 6
但是,如果我给它一个 [1, 2, 3, 3, 4, 5, 6]
的数组,它最终会抛出 WhosebugException。然而,在调试时,我发现它确实达到了新的混洗数组与原始数组匹配的程度,如下所示。
然后继续调用 Shuffle(newArray)
,即使数组中的所有值都相互匹配。
这是什么原因造成的?为什么 Linq 查询 newArray.All(x => x == originalArray[Array.IndexOf(newArray, x)])
return false?
这是一个DotNetFiddle link,其中包括我用来打印结果的代码
您正在比较 object
s。 object
使用与 ==
的引用相等进行比较,而不是值相等。您的示例使用数字,但由于您的代码布局方式,这些数字被隐式装箱到 object
。
为避免这种情况,您应该使用 .Equals()
函数(比较对象时)。
newArray.All(x => x.Equals(originalArray[Array.IndexOf(newArray, x)]))
您还应该在 class 中使用泛型,而不是到处乱扔垃圾 object[]
以确保类型安全 - 除非您使用此洗牌器的目的之一是允许洗牌器洗牌混合类型的数组(这似乎值得怀疑,因为很难从中提取任何有用的信息)。
请注意,每当您比较引用类型时都会出现此行为;仅允许 value 类型传递给您的结构的一种方法(即,只能通过值相等而不是引用相等来比较原始值)是使用 struct
通用约束。例如:
class BadShuffler<T> where T : struct
{
public bool Shuffle(T[] array)
{
...
return newArray.All(x => {
var other = originalArray[Array.IndexOf(originalArray, x)];
return x == other;
});
}
}
这会像您预期的那样工作。
评论中提到的 SequenceEqual
也是一个好主意,因为您的 .All()
调用会说 [1, 2, 3]
等于 [1, 2, 3, 4]
,但是 [1, 2, 3, 4]
将不等于 [1, 2, 3]
- 这两种情况都是不正确的,更重要的是不是可交换的[1],相等操作应该是。
如果您不使用 object[]
,请确保实现您自己的 EqualityComparer。
也就是说,我认为您想结合使用这两种方法并使用 SequenceEqual
和我的方法,除非您需要洗牌对象(即一副纸牌)而不是数字?
作为旁注,我通常建议返回 新,打乱 T[]
而不是修改原始 in-place.
[1]:可交换意味着以一种方式完成的操作可以以相反的方式完成,并且您会得到相同的结果。例如,加法是可交换的:您可以按任何顺序将 1、2 和 3 相加,但结果始终为 6。
考虑以下洗牌方法,给定一个对象数组a
- 从
a
中取出第一个元素并将其放入b
。考虑b
中此元素的索引为x
. - 将
a
中的第二个元素放在b[x]
的前面,这样它现在就在b[x-1]
的位置
- 将
a
中的第三个元素放在b[x]
后面,这样它现在就在b[x+1]
的位置
- 将
a
中的第四个元素放在b[x - 1]的前面,这样它现在就在b[x-2]
的位置
- 将
a
中的第 5 个元素放在b[x+1]
后面,使其现在位于b[x+2]
的位置
- 重复此过程,直到
b
以新的随机顺序包含a
中的所有元素。
我写了一些代码来执行此操作,如下所示。它会在上面的过程中不断对数组进行shuffle,直到shuffle后的数组与原数组匹配,然后return shuffle的次数
public class BadShuffler
{
public BadShuffler(object[] _arrayToShuffle)
{
originalArray = _arrayToShuffle;
Arrays = new List<object[]>
{
originalArray
};
}
private object[] originalArray;
private int count;
public List<object[]> Arrays { get; set; }
public int Shuffle(object[] array = null)
{
if (array == null)
array = originalArray;
count++;
object[] newArray = new object[array.Length];
bool insertAtEnd = false;
int midpoint = newArray.Length / 2;
newArray[midpoint] = array[0];
int newArrayInteger = 1;
int originalArrayInteger = 1;
while (newArray.Any(x => x == null))
{
if (insertAtEnd)
{
newArray[midpoint + newArrayInteger] = array[originalArrayInteger];
newArrayInteger++;
}
else
{
newArray[midpoint - newArrayInteger] = array[originalArrayInteger];
}
originalArrayInteger++;
insertAtEnd = !insertAtEnd;
}
Arrays.Add(newArray);
return (newArray.All(x => x == originalArray[Array.IndexOf(newArray, x)])) ? count : Shuffle(newArray);
}
}
虽然不是世界上最漂亮的东西,但它确实可以。示例如下:
Shuffled 6 times.
1, 2, 3, 4, 5, 6
6, 4, 2, 1, 3, 5
5, 1, 4, 6, 2, 3
3, 6, 1, 5, 4, 2
2, 5, 6, 3, 1, 4
4, 3, 5, 2, 6, 1
1, 2, 3, 4, 5, 6
但是,如果我给它一个 [1, 2, 3, 3, 4, 5, 6]
的数组,它最终会抛出 WhosebugException。然而,在调试时,我发现它确实达到了新的混洗数组与原始数组匹配的程度,如下所示。
然后继续调用 Shuffle(newArray)
,即使数组中的所有值都相互匹配。
这是什么原因造成的?为什么 Linq 查询 newArray.All(x => x == originalArray[Array.IndexOf(newArray, x)])
return false?
这是一个DotNetFiddle link,其中包括我用来打印结果的代码
您正在比较 object
s。 object
使用与 ==
的引用相等进行比较,而不是值相等。您的示例使用数字,但由于您的代码布局方式,这些数字被隐式装箱到 object
。
为避免这种情况,您应该使用 .Equals()
函数(比较对象时)。
newArray.All(x => x.Equals(originalArray[Array.IndexOf(newArray, x)]))
您还应该在 class 中使用泛型,而不是到处乱扔垃圾 object[]
以确保类型安全 - 除非您使用此洗牌器的目的之一是允许洗牌器洗牌混合类型的数组(这似乎值得怀疑,因为很难从中提取任何有用的信息)。
请注意,每当您比较引用类型时都会出现此行为;仅允许 value 类型传递给您的结构的一种方法(即,只能通过值相等而不是引用相等来比较原始值)是使用 struct
通用约束。例如:
class BadShuffler<T> where T : struct
{
public bool Shuffle(T[] array)
{
...
return newArray.All(x => {
var other = originalArray[Array.IndexOf(originalArray, x)];
return x == other;
});
}
}
这会像您预期的那样工作。
评论中提到的SequenceEqual
也是一个好主意,因为您的 .All()
调用会说 [1, 2, 3]
等于 [1, 2, 3, 4]
,但是 [1, 2, 3, 4]
将不等于 [1, 2, 3]
- 这两种情况都是不正确的,更重要的是不是可交换的[1],相等操作应该是。
如果您不使用 object[]
,请确保实现您自己的 EqualityComparer。
也就是说,我认为您想结合使用这两种方法并使用 SequenceEqual
和我的方法,除非您需要洗牌对象(即一副纸牌)而不是数字?
作为旁注,我通常建议返回 新,打乱 T[]
而不是修改原始 in-place.
[1]:可交换意味着以一种方式完成的操作可以以相反的方式完成,并且您会得到相同的结果。例如,加法是可交换的:您可以按任何顺序将 1、2 和 3 相加,但结果始终为 6。