在“自我避免随机游走”的情况下,我有一个带有步长坐标配置的二维向量。我希望能够检查某个站点是否已被占用,但问题是轴可能为零,因此检查坐标的fabs()
是否为true
(或者它是否具有价值),不会奏效。因此,我考虑循环执行这些步骤并检查我的坐标是否等于所有轴上的另一个坐标,如果是,则退回并再次尝试(所谓的深度优先方法)。
有更有效的方法吗?我见过有人使用带有所有可能坐标的布尔数组,如下所示:
bool occupied[nMax][nMax]; // true if lattice site is occupied
for (int y = -rMax; y <= rMax; y++)
for (int x = -rMax; x <= rMax; x++)
occupied[index(y)][index(x)] = false;
但是,在我的程序中维度的数量是未知的,因此会采用如下方法:
typedef std::vector<std::vector<long int>> WalkVec;
WalkVec walk(1, std::vector<long int>(dof,0));
siteVisited = false; counter = 0;
while (counter < (walkVec.back().size()-1))
{
tdof = 1;
while (tdof <= dimensions)
{
if (walkHist.back().at(tdof-1) == walkHist.at(counter).at(tdof-1) || walkHist.back().at(tdof-1) == 0)
{
siteVisited = true;
}
else
{
siteVisited = false;
break;
}
tdof++;
}
工作在哪里dof如果维数。 (如果位置是原点,则检查零检查。同一步骤中的三个零坐标或三个访问坐标是使其成立的唯一方法) 有没有更有效的方法呢?
答案 0 :(得分:3)
您可以分别使用STL的set或unordered_set检查O(log n)或O(1)时间。 unordered_set容器要求您为坐标编写自定义哈希函数,而set容器只需要您提供比较函数。设置实现特别容易,对数时间应该足够快:
#include <iostream>
#include <set>
#include <vector>
#include <cassert>
class Position {
public:
Position(const std::vector<long int> &c)
: m_coords(c) { }
size_t dim() const { return m_coords.size(); }
bool operator <(const Position &b) const {
assert(b.dim() == dim());
for (size_t i = 0; i < dim(); ++i) {
if (m_coords[i] < b.m_coords[i])
return true;
if (m_coords[i] > b.m_coords[i])
return false;
}
return false;
}
private:
std::vector<long int> m_coords;
};
int main(int argc, const char *argv[])
{
std::set<Position> visited;
std::vector<long int> coords(3, 0);
visited.insert(Position(coords));
while (true) {
std::cout << "x, y, z: ";
std::cin >> coords[0] >> coords[1] >> coords[2];
Position candidate(coords);
if (visited.find(candidate) != visited.end())
std::cout << "Aready visited!" << std::endl;
else
visited.insert(candidate);
}
return 0;
}
当然,正如iavr所提到的,任何这些方法都需要O(n)存储。
编辑:这里的基本想法非常简单。目标是以允许您快速检查特定位置是否已被访问的方式存储所有访问过的位置。您的解决方案必须扫描所有访问过的位置以进行此检查,这使得它成为O(n),其中n是访问位置的数量。要更快地完成此操作,您需要一种方法来排除大多数访问过的位置,因此您根本不需要与它们进行比较。
您可以通过考虑对已排序数组进行二进制搜索来理解基于集合的解决方案。首先,您想出一种比较(排序)D维位置的方法。这就是位置类'&lt;运营商正在做。正如iavr在评论中指出的那样,这基本上只是一个词典比较。然后,当所有访问过的位置按此顺序排序时,您可以运行二进制搜索以检查候选点是否已被访问:您递归检查候选点是否会在列表的上半部分或下半部分中找到,从而消除一半每一步比较的剩余清单。在每个步骤中将搜索域减半会给出对数复杂度O(log n)。
STL set容器只是一个很好的数据结构,可以在插入和删除元素时按顺序保存元素,确保插入,删除和查询都很快。如果你很好奇,我使用的STL实现使用一个红黑树来实现这个数据结构,但从你的角度来看,这是无关紧要的;重要的是,一旦你给它一个比较元素(&lt;运算符)的方法,将元素插入集合(set :: insert)并询问元素是否在集合中(set :: find)是O (记录n)。我通过将其添加到访问集来检查原点 - 没有理由特别对待它。
unordered_set是一个哈希表,一个渐近更高效的数据结构(O(1)),但是更难使用,因为你必须编写一个好的哈希函数。此外,对于您的应用程序,从O(n)到O(log n)应该足够好。
答案 1 :(得分:2)
您的问题与算法有关,而与(C ++)语言的使用有关,因此这是一个通用答案。
您需要的是一个数据结构,用于存储一组(点坐标),并使用有效的操作来查询新点是否在集合中。
将集合显式存储为布尔数组可提供恒定时间查询(最快),但在维数为指数的空间中。
详尽的搜索(您的第二个选项)提供了在设置大小(步行长度)中线性的查询,这个查询在设置大小中也是线性的且与维度无关。
另外两个常见选项是树结构和哈希表,例如可用std::set
(通常使用红黑树)和std::unordered_set
(后者仅在C ++ 11中)。树结构通常具有对数时间查询,而哈希表查询在实践中可以是常量时间,几乎使您回到布尔数组的复杂性。但在这两种情况下,所需的空间在集合大小上也是线性的,与维度无关。