最大公共子图算法
创建于:2025年1月24日
创建于:2025年1月24日
现在我给你2个三维点集txt文件,每个txt里大约20多个点,有一些外点,现在你根据图论来找到最大公共子图的问题(距离容忍度小于1.0f),用c++来写
,给个例子给你
点集1228.882 16.0547 455.011
-109.23 0.696136 467.801
-67.0957 -31.6897 455.343
240.973 -33.8383 473.162
-80.4531 -52.4663 449.111
215.14 -52.9228 458.142
245.324 -64.4762 453.444
-34.1375 -62.3761 436.352
93.5639 33.9168 436.248
62.5559 31.8808 434.551
33.6388 23.5955 442.698
-7.17579 2.30447 461.797
217.569 1.1215 469.822
189.098 -27.3088 473.591
28.875 -39.8209 458.772
81.0103 -50.6106 453.151
115.412 -60.2006 448.303
176.543 7.10605 462.899
72.051 -30.03 466.197
43.6684 -62.7862 444.15
110.971 -51.5158 291.299
46.4384 -1.05528 467.759
113.475 -34.6696 464.593
131.984 -72.7575 407.951
88.4232 -17.9225 414.685
41.9902 -24.7438 563.756
-6.46421 -46.5083 717.698
119.659 24.7525 446.406
98.4915 8.03238 462.7
169.479 -67.8302 445.808
148.467 21.1108 448.267
点集2,52.8095 64.5404 423.952
220.929 17.4127 456.935
-75.4857 -27.5634 456.752
232.5 -32.5401 475.295
206.544 -51.4274 460.289
-42.7751 -58.6324 437.99
160.781 -65.9685 447.876
85.8543 36.4761 437.846
54.8313 34.7249 436.096
-15.2865 5.8913 463.28
209.444 2.63013 471.795
20.3893 -36.5948 460.5
72.4548 -47.8794 455.015
35.0173 -59.7505 445.985
236.601 -63.2902 455.663
140.618 23.1976 449.987
168.505 8.96819 464.79
63.6493 -27.1687 467.936
105.041 -32.2064 466.504
25.6765 26.6977 444.225
38.279 2.03565 469.306
90.7028 10.7569 464.253
180.647 -25.5323 475.656
111.916 27.1417 448
106.682 -57.8898 450.357
他们最大公共子图算出来是
Source point: (228.882, 16.0547, 455.011) Target point: (220.929, 17.4127, 456.935) Error: 0.0190912
Source point: (-67.0957, -31.6897, 455.343) Target point: (-75.4857, -27.5634, 456.752) Error: 0.0532388
Source point: (240.973, -33.8383, 473.162) Target point: (232.5, -32.5401, 475.295) Error: 0.0219685
Source point: (215.14, -52.9228, 458.142) Target point: (206.544, -51.4274, 460.289) Error: 0.0148318
Source point: (245.324, -64.4762, 453.444) Target point: (236.601, -63.2902, 455.663) Error: 0.0531482
Source point: (-34.1375, -62.3761, 436.352) Target point: (-42.7751, -58.6324, 437.99) Error: 0.0175656
Source point: (93.5639, 33.9168, 436.248) Target point: (85.8543, 36.4761, 437.846) Error: 0.0403378
Source point: (62.5559, 31.8808, 434.551) Target point: (54.8313, 34.7249, 436.096) Error: 0.0460944
Source point: (33.6388, 23.5955, 442.698) Target point: (25.6765, 26.6977, 444.225) Error: 0.14118
Source point: (-7.17579, 2.30447, 461.797) Target point: (-15.2865, 5.8913, 463.28) Error: 0.040105
Source point: (217.569, 1.1215, 469.822) Target point: (209.444, 2.63013, 471.795) Error: 0.0173551
Source point: (189.098, -27.3088, 473.591) Target point: (180.647, -25.5323, 475.656) Error: 0.0865193
Source point: (28.875, -39.8209, 458.772) Target point: (20.3893, -36.5948, 460.5) Error: 0.0365014
Source point: (81.0103, -50.6106, 453.151) Target point: (72.4548, -47.8794, 455.015) Error: 0.0100752
Source point: (115.412, -60.2006, 448.303) Target point: (106.682, -57.8898, 450.357) Error: 0.145753
Source point: (176.543, 7.10605, 462.899) Target point: (168.505, 8.96819, 464.79) Error: 0.0435401
Source point: (72.051, -30.03, 466.197) Target point: (63.6493, -27.1687, 467.936) Error: 0.0203765
Source point: (43.6684, -62.7862, 444.15) Target point: (35.0173, -59.7505, 445.985) Error: 0.0215593
Source point: (46.4384, -1.05528, 467.759) Target point: (38.279, 2.03565, 469.306) Error: 0.062221
Source point: (113.475, -34.6696, 464.593) Target point: (105.041, -32.2064, 466.504) Error: 0.044152
Source point: (119.659, 24.7525, 446.406) Target point: (111.916, 27.1417, 448) Error: 0.124704
Source point: (98.4915, 8.03238, 462.7) Target point: (90.7028, 10.7569, 464.253) Error: 0.296387
Source point: (169.479, -67.8302, 445.808) Target point: (160.781, -65.9685, 447.876) Error: 0.0498557
Source point: (148.467, 21.1108, 448.267) Target point: (140.618, 23.1976, 449.987) Error: 0.0338657
Found 24
下面给出一个示例 C++ 程序,用“图论+最大团(或最大公共子图)”的思想来在两组 3D 点中寻找最大对应集合。原理简述如下:
读入两组 3D 点
分别从两个 txt 文件中读入三维坐标,保存到 vector<Point3D>
容器中。
构造“兼容图”(Compatibility Graph)
寻找最大团 (Maximum Clique)
输出结果
下面的示例代码演示了这一流程,供参考(为方便演示,代码写得比较直白,未做大规模优化或工程封装;若点数较多,可考虑更高效的数据结构或剪枝策略)。
cpp#include <iostream> #include <fstream> #include <vector> #include <cmath> #include <string> #include <algorithm> //-------------------------------------- // 数据结构定义 //-------------------------------------- struct Point3D { double x, y, z; }; static double distance3D(const Point3D& p1, const Point3D& p2) { double dx = p1.x - p2.x; double dy = p1.y - p2.y; double dz = p1.z - p2.z; return std::sqrt(dx*dx + dy*dy + dz*dz); } //-------------------------------------- // 从TXT文件读取点集,每行格式: x y z //-------------------------------------- std::vector<Point3D> readPointsFromFile(const std::string& filename) { std::vector<Point3D> points; std::ifstream fin(filename); if(!fin.is_open()){ std::cerr << "Failed to open file: " << filename << std::endl; return points; } while(!fin.eof()){ Point3D p; fin >> p.x >> p.y >> p.z; if(!fin.fail()){ points.push_back(p); } } fin.close(); return points; } //-------------------------------------- // 兼容图:节点用 (i, j) 表示第一个点集里的 i 与第二个点集里的 j // adjacency[u] 存放与 u 相连(兼容)的所有节点 v //-------------------------------------- struct PairNode { int i; // 对应第一组点的下标 int j; // 对应第二组点的下标 }; //-------------------------------------- // 在所有 (i, j) 与 (k, l) 之间构图 // 如果距离匹配(误差 < tolerance),则连一条边 //-------------------------------------- std::vector<std::vector<int>> buildCompatibilityGraph( const std::vector<Point3D>& srcPoints, const std::vector<Point3D>& tgtPoints, std::vector<PairNode>& pairNodes, double tolerance) { // 先构造所有可能的配对节点 // pairNodes[u] = (i, j) 表示 srcPoints[i] <-> tgtPoints[j] int N = (int)srcPoints.size(); int M = (int)tgtPoints.size(); pairNodes.clear(); pairNodes.reserve(N*M); for(int i = 0; i < N; ++i) { for(int j = 0; j < M; ++j) { PairNode pn; pn.i = i; pn.j = j; pairNodes.push_back(pn); } } // 构造邻接表 adjacency,大小为 N*M std::vector<std::vector<int>> adjacency(N*M); // 两层循环遍历所有节点对 (u, v),检查距离是否匹配 // 为了减少重复计算,可以先把 srcPoints、tgtPoints 的距离预先缓存下来 // 这里演示就直接现算 for(int u = 0; u < (int)pairNodes.size(); ++u) { for(int v = u + 1; v < (int)pairNodes.size(); ++v) { // pairNodes[u] = (i1, j1) // pairNodes[v] = (i2, j2) const PairNode& pu = pairNodes[u]; const PairNode& pv = pairNodes[v]; double dSrc = distance3D(srcPoints[pu.i], srcPoints[pv.i]); double dTgt = distance3D(tgtPoints[pu.j], tgtPoints[pv.j]); if(std::fabs(dSrc - dTgt) < tolerance) { // 说明这两个配对点对 在距离上“兼容” adjacency[u].push_back(v); adjacency[v].push_back(u); } } } return adjacency; } //-------------------------------------- // 最大团搜索(回溯/Bron–Kerbosch 等方法) // 这里用一个简单的回溯示例,点数较多时可能效率欠佳 //-------------------------------------- void backtrackMaxClique( const std::vector<std::vector<int>>& adjacency, std::vector<int>& currentClique, std::vector<int>& bestClique, const std::vector<int>& candidates, int startPos) { // 剪枝条件:剩余候选加上 currentClique 的大小都不可能超过 bestClique,就可以停止 if((int)currentClique.size() + ((int)candidates.size() - startPos) <= (int)bestClique.size()) { return; } for(int i = startPos; i < (int)candidates.size(); ++i) { int v = candidates[i]; // 检查 v 是否与 currentClique 里的所有点都相连 bool canJoin = true; for(int c : currentClique) { // 如果 c 与 v 不相连,无法继续 // adjacency[c] 应该包含 v auto &adjList = adjacency[c]; if(std::find(adjList.begin(), adjList.end(), v) == adjList.end()) { canJoin = false; break; } } if(canJoin) { // 加入 v 到 currentClique currentClique.push_back(v); // 继续向下搜索 backtrackMaxClique(adjacency, currentClique, bestClique, candidates, i + 1); // 回溯 if(currentClique.size() > bestClique.size()) { bestClique = currentClique; } currentClique.pop_back(); } } } std::vector<int> findMaxClique(const std::vector<std::vector<int>>& adjacency) { // 简单做法:候选就是所有点 0...(n-1) std::vector<int> candidates(adjacency.size()); for(int i = 0; i < (int)adjacency.size(); ++i){ candidates[i] = i; } std::vector<int> bestClique; std::vector<int> currentClique; backtrackMaxClique(adjacency, currentClique, bestClique, candidates, 0); return bestClique; } //-------------------------------------- // 主流程示例 //-------------------------------------- int main() { // 假设我们有两个文件,分别存放两组点 std::string fileA = "pointsA.txt"; std::string fileB = "pointsB.txt"; // 1) 读入两组点 auto srcPoints = readPointsFromFile(fileA); auto tgtPoints = readPointsFromFile(fileB); std::cout << "Read " << srcPoints.size() << " points from " << fileA << std::endl; std::cout << "Read " << tgtPoints.size() << " points from " << fileB << std::endl; if(srcPoints.empty() || tgtPoints.empty()){ std::cerr << "One of the point sets is empty, abort." << std::endl; return -1; } // 2) 构造兼容图 double tolerance = 1.0; // 距离容忍度 std::vector<PairNode> pairNodes; // (i,j) 列表 auto adjacency = buildCompatibilityGraph(srcPoints, tgtPoints, pairNodes, tolerance); // 3) 寻找最大团 std::vector<int> maxClique = findMaxClique(adjacency); // 4) 输出结果 std::cout << "Max clique size = " << maxClique.size() << std::endl; // 对应点对 for(int idx : maxClique) { const auto& pn = pairNodes[idx]; const Point3D& spt = srcPoints[pn.i]; const Point3D& tpt = tgtPoints[pn.j]; // 计算两点的绝对误差(只做一个距离比较,也可以单独输出每对点坐标的差) double err = std::fabs(distance3D(spt, spt) - distance3D(tpt, tpt)); // 事实上 distance3D(spt, spt) = 0, distance3D(tpt, tpt) = 0,这里仅演示,可以改为其它误差度量 // 通常更有意义的是把 S_i 到 T_j 的坐标差或其他误差算出来 // 这里可以改成:err = distance3D(spt, tpt),看它们相差多大(若你想看对应点本身是否也相近等等) // 或者仅打印出 i, j 即可。 // 演示:直接打印配对以及它们各自坐标 std::cout << "Source point [" << pn.i << "] = (" << spt.x << ", " << spt.y << ", " << spt.z << ") " << "Target point [" << pn.j << "] = (" << tpt.x << ", " << tpt.y << ", " << tpt.z << ") " << "Error(demo) = " << err << std::endl; } return 0; }
数据结构
Point3D
用来存储点的坐标。PairNode (i, j)
代表第一组点里下标为 i
的点、与第二组点里下标为 j
的点构成的一对。构造“兼容图”
N*M
个“候选节点”,遍历两两之间 和 :
adjacency
。最大团搜索
输出结果
maxClique
中,每个元素是一个索引 idx
,对应 pairNodes[idx] = (i, j)
。i
个点,与目标点集中第 j
个点,存在一致的距离关系,从而匹配上了。如果你想像示例结果那样,输出每一对匹配点的坐标、并计算它们之间的空间误差(例如直接看它们的欧式距离是否也在一定范围内),可在最终输出的时候做更多计算。
distance3D(spt, spt)
这类占位写法。具体请根据你要衡量的“误差定义”作调整。这样,你就可以根据这段代码,针对两组 3D 点(文本文件输入),构建兼容图并找出最大公共子图对应的匹配关系了。祝开发顺利!