【问题标题】:Equality test function等式测试功能
【发布时间】:2019-01-17 20:29:32
【问题描述】:

下面是一个函数,旨在在一维向量中的相邻数字之间执行相等测试。

这个一维向量将具有代表 nxn 网格的值。 [ v 是向量]

当它们相等时返回 false。

例如考虑这个 3x3 网格:

i\j| 0 | 1 | 2
0  | 1 | 2 | 3
1  | 4 | 5 | 6
2  | 7 | 8 | 9

我编写的代码的问题是,并非网格中的所有数字都会有 4 个其他相邻数字并测试不存在的索引,例如在尝试比较网格中左上角数字上方的数字时(示例中的 1)可能会导致一些不准确的结果。

除此之外,我写的似乎不是最有效的方法。肯定有比列出 5 个新变量更简单的方法吗?

for( int i= 0; i < n ; i++ ){
    for( int j = 0; j < n; j++){
        int x = v[convert(i, j, n)];
        int c = v[convert(i-1, j, n)];
        int s = v[convert(i+1, j, n)];
        int b = v[convert(i, j+1, n)];
        int n = v[convert(i, j-1, n)];

        if (x == c || x == s || x == b || x == n ) {
            return false;
        }
    }
}

//another function used to convert 2d into 1D index
 int convert(int row, int col, int rowlen){
    return row*rowlen+col;
}

我将不胜感激。

【问题讨论】:

  • 一般情况下,避免绑定检查的另一种方法是用“哨兵”包围阵列(将 3x3 阵列转换为 5x5 阵列),并在阵列的“中心”正常工作。

标签: c++ for-loop if-statement vector


【解决方案1】:

如果您想要一种有效的方法来执行此操作,则应考虑值的缓存局部性、执行多少索引转换、执行多少边界测试以及需要多少比较。

首先要注意的是,当您已经在与右侧和下方进行比较时,您不需要与左侧和上方进行比较。这是因为左/上测试将在下一次迭代的右/下测试时发生。如此立即,测试量减半。

第一个优化是将操作拆分为行测试和列测试:

// Test adjacency in rows
for (const int *rowptr = v, *end = v + n * n;
     rowptr != end;
     rowptr += n)
{
    for (int col = 1; col < n; col++) {
        if (rowptr[col-1] == rowptr[col]) return false;
    }
}

// Test adjacency in columns
for (const int *row0ptr = v, *row1ptr = v + n, *end = v + n * n;
     row1ptr != end;
     row0ptr = row1ptr, row1ptr += n)
{
    for (int col = 0; col < n; col++) {
        if (row0ptr[col] == row1ptr[col]) return false;
    }
}

为了避免两次遍历整个数组,您需要将它们组合起来,但它开始变得有点混乱。请注意当前两个单独的传递如何具有不同的边界(行测试从第 1 列循环到第 n 列,而列测试从第 0 行循环到第 n-1 行)。

只有当n 非常大并且这段代码的速度绝对关键时,组合循环才有意义。这个想法是对整个数组执行一次遍历,避免在第二次遍历时出现 L1 缓存未命中等问题。

看起来像这样:

const int *row0ptr = v, *row1ptr = v + n, *end = v + n * n
for ( ; row1ptr != end; row0ptr = row1ptr, row1ptr += n)
{
    // Test first column
    if (row0ptr[0] == row1ptr[0]) return false;

    // Test row0 and remaining columns
    for (int col = 1; col < n; col++) {
        if (row0ptr[col-1] == row0ptr[col]) return false;
        if (row0ptr[col] == row1ptr[col]) return false;
    }
}

// Test last row
for (int col = 1; col < n; col++) {
    if (row0ptr[col-1] == row0ptr[col]) return false;
}

【讨论】:

  • 感谢您的评论。抱歉,我是 C++ 新手,我不熟悉这里的代码风格(再次道歉)。为什么测试 left 也可以让我们测试 right 和 up 和 down 反之亦然?你能解释一下第一个 for 循环的代码吗?非常感谢。
  • 好的,考虑x位置的左右对:(x-1, x) 和 (x, x+1)。如果 x 为 0,则由于数组边界,您只能在右侧测试,因此您测试 (0,1)。在下一次迭代中,x 为 1。如果您在左侧测试,您正在测试 (0,1),它已经在上一次迭代中测试为在右侧。所以是多余的。列也是如此。
  • 我明白你的意思了。问题是您需要检查每个数字及其在网格中的所有相邻数字。上述方法不会检查网格中每个数字的相邻数字。
  • 是的。想想吧。
  • 刚刚在我的脑海里想了一遍。说得通。谢谢。
【解决方案2】:

首先我建议打破逻辑,因为它变得非常复杂。但是这样的事情是可行的,它通过在ij 上添加额外的检查来避免超出网格,并且可以避免对convert 的不必要调用,因为如果早期测试之一为真,则不会执行后面的测试.

     int x = v[convert(i, j, n)];
     if (i > 0 && x == v[convert(i-1, j, n)])
         return false;
     if (i < n - 1 && x == v[convert(i+1, j, n)])
         return false;
     if (j > 0 && x == v[convert(i, j-1, n)])
         return false;
     if (j < n - 1 && x == v[convert(i, j+1, n)])
         return false;

【讨论】:

  • 感谢您的帖子。这有助于解决网格中不存在索引的问题。考虑到这有 2 个 for 循环和 4 个 if 语句,还有其他方法可以更有效地解决这个问题吗?
  • @James 我相信你可以稍微调整一下,但问题的本质不是吗?您需要遍历网格,并且需要在每个坐标处进行四次测试。
  • @James 四个ifs 或一个if 和四个条件......可能没有区别。选择更容易阅读的内容。请注意,这种布局可以保护代码免受问题中甚至没有考虑的一大堆情况的影响,并且如果函数提前退出,则可以消除许多额外的工作。这是一个多层次的胜利。
  • @user4581301 你指的是什么情况?
  • 正如我在回答中指出的那样,您不需要在所有四个方面都进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-23
  • 2020-12-09
  • 1970-01-01
相关资源
最近更新 更多