洗牌矩阵

Shuffling a matrix

我需要用给定数量的特定值随机填充矩阵。 (出于某种原因,我有一个 C 二维数组)。我找到的简单解决方案是将二维数组解释为一维数组:(请忽略硬编码常量和临时随机对象):

int m[10][10] = {};

std::fill_n(&m[0][0], 24, -1);
std::shuffle(&m[0][0], &m[0][0] + 100, std::mt19937{std::random_device{}()});

但这似乎是有争议的,因为它是否是未定义的行为:

Is it legal to access a bidimensional array as if it where a one-dimensional one?

May I treat a 2D array as a contiguous 1D array?

要点是,即使保证基础数据在行之间没有填充的情况下是连续的,索引方案或在第一行之外递增 &m[0][0] int* 指针也是无效的。

所以我正在寻找一种替代的安全方法。有没有一种简单的方法来填充然后打乱二维数组,而无需创建一维数组然后将其复制到矩阵中?

注意:矩阵的其余部分都是 0,因此不需要保留这些单元格。

Is there a simple way to fill and then shuffle the 2D array without creating a 1D array

当然可以。假设你已经填充了数组,要对其进行洗牌,为什么不随机生成 i1 ,i2, j1, j2 其中 i 和 j 是 限制 行和列。现在交换这些索引的值。这样说 0.5 x no.of.rows x no.of.cols ,这只是一个非常粗略的数字。基本上可以随心所欲地随机化它。任何小于 10^8 的都可以。

如果您担心生成重复对(对于较大的矩阵这应该不是真正的问题),您可以维护一个 std::set<pair<int, int> > 来存储生成的索引。每当您生成 ij 时,请检查 {i, j} 是否存在。如果是,请重新生成它。

您可以在整数(您的一维)索引和 (i,j) 对之间选择双射函数,因此这将是将您的矩阵视为一维数组的方式。这叫做 'numbering scheme'.

类似于:

oneDindex(rowIndex,colIndex)=colIndex*rowCount+rowIndex;

逆变换

rowIndex=oneDIndex % rowCount

colIndex=oneDIndex / rowCount;

所以,你在[0, rowCount*colCount)中随机选择两个你想要交换的一维索引,通过逆变换得到它们的(rowIndex,colIndex)并交换它们。重复直到满意。

假设您的二维数组的维度为 m X n,它已被初始化,并且您想进行就地随机洗牌。

为此修改 Durstenfeld variant of the Fisher-Yates Shuffling algorithm 非常容易。以下是伪代码:

for i = m * n - 1 downto 1
    j = random integer in the range [0, i] (both inclusive)
    swap(a[i / n][i % n], a[j / n][j % n])

这本质上是原始算法,将数组视为 1d。但是,无论何时选择索引 ij,都会在对每个索引进行操作之前将其转换为行 + 列。