简体   繁体   English

有没有办法递归遍历矩阵的所有可能子矩阵,同时防止访问某些子矩阵?

[英]Is there a way to recursively iterate through all possible sub-matrices of a matrix while preventing some sub-matrices from being visited?

My task is to find all sub-matrices inside a matrix such that each sub-matrix counted satisfies a certain condition and also is not a part of another sub-matrix that works.我的任务是找到矩阵内的所有子矩阵,使得每个计数的子矩阵都满足特定条件,并且也不属于另一个有效子矩阵的一部分。

My first thought was to write a recursive procedure so that we could simply return from the current sub-matrix whenever we find that it works (to prevent any sub-matrices of that sub-matrix from being tested).我的第一个想法是编写一个递归过程,以便我们可以在发现当前子矩阵有效时简单地从当前子矩阵return (以防止对该子矩阵的任何子矩阵进行测试)。 Here is my code that attempts to do this:这是我尝试执行此操作的代码:

void find(int xmin, int xmax, int ymin, int ymax){
    if(xmin > xmax || ymin > ymax){return;}
    else if(works(xmin, xmax, ymin, ymax)){++ANS; return;}

    find(xmin + 1, xmax, ymin, ymax);
    find(xmin, xmax - 1, ymin, ymax);
    find(xmin, xmax, ymin + 1, ymax);
    find(xmin, xmax, ymin, ymax - 1);
}

The problem with my current method seems to be the fact that it allows sub-matrices to be visited more than once, meaning that the return statements are ineffective and don't actually prevent sub-matrices of working sub-matrices from being counted, because they are visited from other matrices.我当前方法的问题似乎在于它允许多次访问子矩阵,这意味着return语句无效并且实际上并没有阻止工作子矩阵的子矩阵被计数,因为从其他矩阵访问它们。 I think I have the right idea with writing a recursive procedure, though.不过,我认为我编写递归程序的想法是正确的。 Can someone please point me in the right direction?有人可以指出我正确的方向吗?

Obviously, you need a way to check if a submatrix was evaluated before or is contained in a larger solution.显然,您需要一种方法来检查子矩阵是否在之前评估过或包含在更大的解决方案中。 Also, you need to account that after a solution is found, a larger solution may be found later which covers the currently found solution.此外,您需要考虑到在找到解决方案后,稍后可能会找到一个更大的解决方案,它涵盖了当前找到的解决方案。

One way of doing this is to utilize a structure called R*-tree , which allows to query spatial (or geometric) data efficiently.这样做的一种方法是利用称为R*-tree的结构,它允许有效地查询空间(或几何)数据。 For this purpose, you could use R-tree implementation from boost .为此,您可以使用boost 中的 R-tree 实现 By using a box (rectangle) type to represent a submatrix, you can then use the R-tree with queries boost::geometry::index::contains (to find previously found solutions which include the submatrix considered) and boost::geometry::index::within (to find previously found solutions which are contained within a newly found solution).通过使用框(矩形)类型来表示子矩阵,然后您可以将 R 树与查询boost::geometry::index::contains (以查找包含所考虑的子矩阵的先前找到的解决方案)和boost::geometry::index::within (查找包含在新找到的解决方案中的先前找到的解决方案)。

Here is a working example in C++11, which is based on your idea:这是 C++11 中的一个工作示例,它基于您的想法:

#include <vector>
#include <numeric>
#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/box.hpp>
#include <boost/geometry/index/rtree.hpp>

namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

struct Element
{
    int x, y;
};

struct Box
{
    Element lt, rb;
};

BOOST_GEOMETRY_REGISTER_POINT_2D(Element, long, cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_BOX(Box, Element, lt, rb)

template<class M>
bool works(M&& matrix, Box box) {
    // Dummy function to check if sum of a submatrix is divisible by 7
    long s = 0;
    for (int y=box.lt.y; y<box.rb.y; y++)
        for (int x=box.lt.x; x<box.rb.x; x++)
            s += matrix[y][x];
    return s % 7 == 0;
}

template <class T, class M>
void find(T& tree, M&& matrix, Box box, T& result) {
    if (box.lt.x >= box.rb.x || box.lt.y >= box.rb.y) return;
    for (auto it = tree.qbegin(bgi::contains(box)); it != tree.qend(); ++it) return;
    if (works(matrix, box)) {
        // Found a new solution

        // Remove any working submatrices which are within the new solution
        std::vector<Box> temp;
        for (auto it = result.qbegin(bgi::within(box)); it != result.qend(); ++it)
            temp.push_back(*it); 
        result.remove(std::begin(temp), std::end(temp));

        // Remember the new solution
        result.insert(box);
        tree.insert(box);
        return;
    }

    // Recursion
    find(tree, matrix, Box{{box.lt.x+1,box.lt.y},{box.rb.x,box.rb.y}}, result);
    find(tree, matrix, Box{{box.lt.x,box.lt.y+1},{box.rb.x,box.rb.y}}, result);
    find(tree, matrix, Box{{box.lt.x,box.lt.y},{box.rb.x-1,box.rb.y}}, result);
    find(tree, matrix, Box{{box.lt.x,box.lt.y},{box.rb.x,box.rb.y-1}}, result);

    tree.insert(box);
}

template <class T>
void show(const T& vec) {
    for (auto box : vec) {
        std::cout << "Start at (" << box.lt.x << ", " << box.lt.y << "), width="
                  << box.rb.x-box.lt.x << ", height=" << box.rb.y-box.lt.y << "\n";
    }
}

int main()
{
    // Initialize R-tree
    const size_t rtree_max_size = 20000;
    bgi::rtree<Box, bgi::rstar<rtree_max_size> > tree, result;

    // Initialize sample matrix
    const int width = 4;
    const int height = 3;
    int matrix[height][width];
    std::iota((int*)matrix, (int*)matrix + height*width, 1);

    // Calculate result
    find(tree, matrix, Box{{0,0},{width,height}}, result);

    // Output
    std::cout << "Resulting submatrices:\n";
    show(result);

    return 0;
}

In this example the following matrix is considered:在本例中,考虑了以下矩阵:

1  2  3  4
5  6  7  8
9 10 11 12

And the program will find all submatrices for which the sum of their elements is divisible by 7. Output:程序将找到所有元素之和可被 7 整除的子矩阵。输出:

Resulting submatrices:
Start at (0, 2), width=4, height=1
Start at (1, 0), width=3, height=3
Start at (0, 0), width=2, height=2
Start at (0, 1), width=1, height=2

What I liked about your recursive approach is that it works very fast even for large matrices of 1000×1000 elements.我喜欢你的递归方法的一点是,即使对于 1000×1000 元素的大型矩阵,它也能运行得非常快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM