【发布时间】:2021-02-15 14:58:27
【问题描述】:
我有一大组二进制字符串,长度都一样。我知道每个二进制字符串的 1 步邻域(意思是,所有其他二进制字符串仅相差 1 位)。这种关系本质上创建了一个图表。现在,从图中的任何节点开始,我想知道在基于某些派生值(不是基于二进制字符串)到达不同节点之前我必须采取的最小步骤数。我正在使用一些下面的示例中的虚拟函数 GetABC() )。如果我们在找到不同节点之前遇到死胡同,我们假设到达不同节点的最小步数是无限的。死胡同是指无法访问新节点(基于访问节点的历史)。如果我们的整个种群具有相同的派生值,那么最坏的情况是我们会访问每个节点然后返回无穷大 (-1)。
我的第一次尝试是使用递归函数,并通过传递对访问节点向量的引用来跟踪所有访问节点。它适用于小型数据集,但使用 100 万个二进制字符串时,我遇到了堆栈溢出错误(字面意思)。
然后,我重构了我的代码以循环运行,而不是通过迭代构建一组要访问的邻居然后遍历所有邻居并检查是否有任何具有不同的派生值 (GetABC()),然后从所有我们刚去过的邻居。同样,在一个小型数据集上工作,但有 100 万条记录,我的代码已经运行了 4 天,但仍未完成(在 12 个线程上使用并行 for_each 对所有节点进行此分析)。
//Previously had this function as a recursive algorithm, but it triggered stack overflow on big dataset
int MyClass::FindDifferentNodeInNeighborhood2(uint64_t node_hash) {
std::vector<uint64_t> visited;//init empty visited vector
visited.push_back(node_hash);
int level = 0;
bool found_diff_node = false;
std::unordered_map<int, std::vector<std::pair<uint64_t, uint64_t>>> to_visit;//<level, vec<node_to_visit, parent_node>>
to_visit[level].push_back(std::make_pair(node_hash, 0));//0 because no parent. Not used in first loop anyway so the value doesn't matter
while (!found_diff_node && !to_visit[level].empty()) {
for (auto n : to_visit[level]) {
//_negihborhood is a member variable of MyClass defined as
//std::unordered_map<uint64_t, std::unordered_set<uint64_t>> _neighborhood;
for (const auto neighbor : _neighborhood[n.first]) {
//make sure we didn't visit this neighbor already
if (std::find(visited.begin(), visited.end(), neighbor) == visited.end()) {
//if not, add to next set of node to visit
to_visit[level + 1].push_back(std::make_pair(neighbor, n.first));
}
}
}
level++;
for (auto n : to_visit[level]) {
//auto node = n.first;
//auto parent = n.second;
visited.push_back(n.first);
//_population is the map of the actual binary strings objects
//We retrieve the object using it's hash
//GetABC() is just a function that gets a specific value from the node
if (_population[n.first].GetABC() != _population[n.second].GetABC()) {
found_diff_node = true;
break;
}
}
}
if (!found_diff_node) {
return -1;
}
return level;
}
我会进一步调试,看看它是否不是无限循环的问题,但我想我应该在这里问我是否没有忽略一些可以实现这一点的简单算法。
谢谢
【问题讨论】:
-
您正在使用哪些编译器选项(具体来说,您是否启用了优化)?
-
这里有很多东西要解压。 1) 数百万 记录? 有多少 节点? 如果您正在搜索每个字符串的一步邻域,那么您的搜索空间很大,并且有可用的捷径. 2)是的,在这种情况下递归是一种不好的方法。 3)你的实际算法是什么?你熟悉 Dijkstra 吗?
-
@1201ProgramAlarm 我正在使用 MSVC 2019 编译,使用已启用优化的发布配置(不记得是 O2 还是 O3)
-
@Beta 好问题。每条记录 1 个节点,因此 100 万个节点。实际上,构建一步邻域的地图很好,问题实际上是检查 n 步邻域,直到派生值不同。我不知道我实现的算法的名称是什么,但它与 Dijkstra 有点不同,虽然结果是一样的
标签: c++ optimization graph-traversal