查找一个二维矩阵是否是另一个二维矩阵的子集

2024-05-23

最近我参加了一个黑客马拉松,我了解到一个问题,试图在 2d 矩阵中找到网格形式的模式。模式可以是 U、H 和 T,并由 3*3 矩阵表示 假设我想展示 H 和 U

+--+--+--+            +--+--+--+
|1 |0 |1 |            |1 |0 |1 |
+--+--+--+            +--+--+--+
|1 |1 |1 |  --> H     |1 |0 |1 |    -> U
+--+--+--+            +--+--+--+
|1 |0 |1 |            |1 |1 |1 |
+--+--+--+            +--+--+--+

现在我需要搜索这个10*10 matrix containing 0s and 1s.最接近且唯一的解决方案,我可以得到 O(n^4) 的强力算法。在 MATLAB 和 R 等语言中,有非常微妙的方法可以做到这一点,但在 C、C++ 中却不行。我尝试了很多在 Google 和 SO 上搜索这个解决方案。但我能得到的最接近的是这个SO POST https://stackoverflow.com/questions/1975386/fast-counting-of-2d-sub-matrices-withing-a-large-dense-2d-matrix其中讨论了实施Rabin-Karp 字符串搜索算法但没有伪代码或任何帖子解释这一点。任何人都可以帮助或提供任何链接、pdf 或一些逻辑来简化这一点吗?

EDIT

作为尤金·Sh.评论说,如果 N 是大矩阵的大小 (NxN),k - 小矩阵的大小 (kxk),则 buteforce 算法应该采用 O((Nk)^2)。由于 k 是固定的,它会减少到 O(N^2)。是的,绝对正确。 但是如果N和K很大,有没有通用的方法呢?


好吧,这是 2D拉宾-卡普 https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm方法。

对于下面的讨论,假设我们想要在 (n, n) 矩阵。 (这个概念也适用于矩形矩阵,但我用完了索引。)

这个想法是,对于每个可能的子矩阵,我们计算一个散列。仅当该散列与我们想要查找的矩阵的散列匹配时,我们才会按元素进行比较。

为了提高效率,我们必须避免每次重新计算子矩阵的整个散列。因为我今晚睡得很少,所以我唯一能弄清楚如何轻松做到这一点的哈希函数是各个子矩阵中 1 的总和。我把它留给比我聪明的人作为练习,以找出更好的滚动哈希函数。

现在,如果我们刚刚检查了从 (i, j) 到 (i + m – 1, j + m – 1) 并且知道里面有x个1,我们可以计算子矩阵中1的数量向右一位 – 即从 (i, j + 1) 到 (i + m) – 1, j + m) – 从 (i, j 中减去子向量中 1 的数量>) 到 (i + m – 1, j) 并添加来自 (i>i , j + m) 到 (i + m – 1, j + m)。

如果我们到达大矩阵的右边距,我们将窗口向下移动一位,然后回到左边距,然后再次向下移动一位,然后再次向右移动,依此类推。

Note that this requires O(m) operations, not O(m2) for each candidate. If we do this for every pair of indices, we get O(mn2) work. Thus, by cleverly shifting a window of the size of the potential sub-matrix through the large matrix, we can reduce the amount of work by a factor of m. That is, if we don't get too many hash collisions.

这是一张图片:

当我们将当前窗口右移一位时,减去左边红色列向量中1的个数,加上右边绿色列向量中1的个数,得到新窗口中的1个数。窗户。

我已经使用伟大的实现了这个想法的快速演示Eigen http://eigen.tuxfamily.org/index.php?title=Main_PageC++ 模板库。该示例还使用了 Boost 中的一些内容,但仅用于参数解析和输出格式化,因此如果您没有 Boost 但想尝试代码,您可以轻松摆脱它。索引摆弄有点混乱,但我将不做进一步解释。上面的散文应该足以涵盖它。

#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <random>
#include <type_traits>
#include <utility>

#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>

#include <Eigen/Dense>

#define PROGRAM "submatrix"
#define SEED_CSTDLIB_RAND 1

using BitMatrix = Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic>;
using Index1D = BitMatrix::Index;
using Index2D = std::pair<Index1D, Index1D>;

std::ostream&
operator<<(std::ostream& out, const Index2D& idx)
{
  out << "(" << idx.first << ", " << idx.second << ")";
  return out;
}

BitMatrix
get_random_bit_matrix(const Index1D rows, const Index1D cols)
{
  auto matrix = BitMatrix {rows, cols};
  matrix.setRandom();
  return matrix;
}

Index2D
findSubMatrix(const BitMatrix& haystack,
              const BitMatrix& needle,
              Index1D *const collisions_ptr = nullptr) noexcept
{
  static_assert(std::is_signed<Index1D>::value, "unsigned index type");
  const auto end = Index2D {haystack.rows(), haystack.cols()};
  const auto hr = haystack.rows();
  const auto hc = haystack.cols();
  const auto nr = needle.rows();
  const auto nc = needle.cols();
  if (nr > hr || nr > hc)
    return end;
  const auto target = needle.count();
  auto current = haystack.block(0, 0, nr - 1, nc).count();
  auto j = Index1D {0};
  for (auto i = Index1D {0}; i <= hr - nr; ++i)
    {
      if (j == 0)  // at left margin
        current += haystack.block(i + nr - 1, 0, 1, nc).count();
      else if (j == hc - nc)  // at right margin
        current += haystack.block(i + nr - 1, hc - nc, 1, nc).count();
      else
        assert(!"this should never happen");
      while (true)
        {
          if (i % 2 == 0)  // moving right
            {
              if (j > 0)
                current += haystack.block(i, j + nc - 1, nr, 1).count();
            }
          else  // moving left
            {
              if (j < hc - nc)
                current += haystack.block(i, j, nr, 1).count();
            }
          assert(haystack.block(i, j, nr, nc).count() == current);
          if (current == target)
            {
              // TODO: There must be a better way than using cwiseEqual().
              if (haystack.block(i, j, nr, nc).cwiseEqual(needle).all())
                return Index2D {i, j};
              else if (collisions_ptr)
                *collisions_ptr += 1;
            }
          if (i % 2 == 0)  // moving right
            {
              if (j < hc - nc)
                {
                  current -= haystack.block(i, j, nr, 1).count();
                  ++j;
                }
              else break;
            }
          else  // moving left
            {
              if (j > 0)
                {
                  current -= haystack.block(i, j + nc - 1, nr, 1).count();
                  --j;
                }
              else break;
            }
        }
      if (i % 2 == 0)  // at right margin
        current -= haystack.block(i, hc - nc, 1, nc).count();
      else  // at left margin
        current -= haystack.block(i, 0, 1, nc).count();
    }
  return end;
}

int
main(int argc, char * * argv)
{
  if (SEED_CSTDLIB_RAND)
    {
      std::random_device rnddev {};
      srand(rnddev());
    }
  if (argc != 5)
    {
      std::cerr << "usage: " << PROGRAM
                << " ROWS_HAYSTACK COLUMNS_HAYSTACK"
                << " ROWS_NEEDLE COLUMNS_NEEDLE"
                << std::endl;
      return EXIT_FAILURE;
    }
  auto hr = boost::lexical_cast<Index1D>(argv[1]);
  auto hc = boost::lexical_cast<Index1D>(argv[2]);
  auto nr = boost::lexical_cast<Index1D>(argv[3]);
  auto nc = boost::lexical_cast<Index1D>(argv[4]);
  const auto haystack = get_random_bit_matrix(hr, hc);
  const auto needle = get_random_bit_matrix(nr, nc);
  auto collisions = Index1D {};
  const auto idx = findSubMatrix(haystack, needle, &collisions);
  const auto end = Index2D {haystack.rows(), haystack.cols()};
  std::cout << "This is the haystack:\n\n" << haystack << "\n\n";
  std::cout << "This is the needle:\n\n" << needle << "\n\n";
  if (idx != end)
    std::cout << "Found as sub-matrix at " << idx << ".\n";
  else
    std::cout << "Not found as sub-matrix.\n";
  std::cout << boost::format("There were %d (%.2f %%) hash collisions.\n")
    % collisions
    % (100.0 * collisions / ((hr - nr) * (hc - nc)));
  return (idx != end) ? EXIT_SUCCESS : EXIT_FAILURE;
}

当它编译和运行时,请将上面的内容视为伪代码。我几乎没有尝试优化它。这只是我自己的一个概念验证。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

查找一个二维矩阵是否是另一个二维矩阵的子集 的相关文章

  • 对静态成员变量的未定义引用

    我有一个有静态成员的类 它也是我的程序中其他几个类的基类 这是它的头文件 ifndef YARL OBJECT HPP define YARL OBJECT HPP namespace yarlObject class YarlObject
  • 如何动态加载包含非托管代码的原始程序集?(绕过“无法验证的代码失败策略检查”异常)

    我将举一个使用的例子系统 Data SQLite DLL http sqlite phxsoftware com 这是一个包含非托管代码的混合程序集 如果我执行这个 var assembly Assembly LoadFrom System
  • 如何在线程创建和退出时调用函数?

    include
  • 每次调用新方法时触发事件

    我正在做一个logger for a c 应用程序需要记录每个方法被调用的时间以及每个方法执行时间 我可以通过调用自己的方法来做到这一点EventLogger LogMethodCall方法在每个方法的开头 但我想知道是否有办法使CLR每次
  • 如何启动异步任务对象

    我想开始收集Task同时处理对象并等待所有对象完成 下面的代码显示了我想要的行为 public class Program class TaskTest private Task createPauseTask int ms works w
  • 有没有办法将 boost::json::serializer 切换为美化输出?

    Using boost json serializer如中的示例所示文档 快速查看 http vinniefalco github io doc json json usage quick look html以紧凑格式保存 json tre
  • 如何使用boost库读取和写入.ini文件[重复]

    这个问题在这里已经有答案了 如何使用boost库读取和写入 或修改 ini文件 With Boost PropertyTree您可以读取并更新树 然后写入文件 请参阅load and save功能 看一下如何访问属性树中的数据 http w
  • 在 C++ 中使用表达式模板进行符号微分

    如何在 C 中使用表达式模板实现符号微分 一般来说 您需要一种表示符号的方法 即编码的表达式模板 例如3 x x 42 以及一个可以计算导数的元函数 希望您对 C 中的元编程足够熟悉 知道这意味着什么和需要什么 但可以给您一个想法 This
  • C++:初始化静态字符串成员

    我在 C 中初始化静态字符串成员时遇到一些问题 我有几个类 每个类都包含几个表示 id 的静态字符串成员 当我通过调用静态函数初始化变量时 一切都很好 但是 当我想为一个变量分配另一个变量的值时 它仍然保留空字符串 这段代码有什么问题 st
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • 如何强制用户仅使用“new”创建从我派生的类的对象?

    为了实现引用计数 我们使用IUnknown http msdn microsoft com en us library ms680509 VS 85 aspx类接口和智能指针模板类 该接口具有所有引用计数方法的实现 包括Release vo
  • 将一个整数从 C 客户端发送到 Java 服务器

    我使用此代码将一个整数从我的 Java 客户端发送到我的 Java 服务器 int n rand nextInt 50 1 DataOutputStream dos new DataOutputStream socket getOutput
  • 按值返回的函数的返回语句中的初始化

    我的问题源于深入研究std move in return语句 例如以下示例 struct A A std cout lt lt Constructed lt lt this lt lt std endl A A noexcept std c
  • 数组与映射的性能

    我必须循环一个大数组中的元素子集 其中每个元素都指向另一个元素 问题来自于检测大图中的连接组件 我的算法如下 1 考虑第一个元素 2 将下一个元素视为前一个元素所指向的元素 3 循环直到没有发现新元素 4 考虑1 3中尚未考虑的下一个元素
  • char* argv[] 在 c/c++ 中如何工作? [复制]

    这个问题在这里已经有答案了 我知道它用于使用命令行中的参数 但我没有得到声明 字符 argv 它是否意味着指向 char 数组的指针 如果是的话为什么没有大小 如果不是动态数组 就不需要有大小吗 我做了一些研究 发现有人说它会衰减为 cha
  • 从 exit() 和 fork() 返回的结果奇怪地发生了位移

    我有一个 C 代码 有时会自行分叉 每个分叉都会执行一些操作 然后返回一个错误代码 目前 每个子进程返回其 ID 0 n void other int numero exit numero int main for int i 0 i lt
  • 为什么调试器只显示数组指针中的一个元素?

    首先 我知道new是执行此操作的 C 方法 我只是表明有不止一种方法可以重现此错误 而且两种方法都令人难以置信的令人沮丧 我有两种形式的源文件 我正在尝试调试另一个编程作业 但我并没有寻求帮助 基本上 我正在尝试重新实施set作为一个类 具
  • 如何求两个地点的经纬度距离?

    我有一组位置的纬度和经度 怎么找distance从集合中的一个位置到另一个位置 有公式吗 半正矢公式假定地球是球形的 然而 地球的形状更为复杂 扁球体模型会给出更好的结果 如果需要这样的精度 你应该更好地使用文森特逆公式 See http
  • 扔掉挥发物安全吗?

    大多数时候 我都是这样做的 class a public a i 100 OK delete int j Compiler happy But is it safe The following code will lead compilat
  • 如何从尖点库矩阵格式获取原始指针

    我需要从尖点库矩阵格式获取原始指针 例如 cusp coo matrix

随机推荐