我给你一个算法来找出给定距离矩阵的 N 个最近邻居。我不会进一步猜测你对分子化合物的建模,所以我使用的是一种非常简化的方法。
#include <iostream>
#include <cmath>
#include <vector>
#include <map>
#include <limits>
struct Point3D {
double x;
double y;
double z;
};
double squareDistance(const Point3D &a, const Point3D &b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
double dz = a.z - b.z;
return dx*dx + dy*dy + dz*dz;
}
struct Molecule {
// more meaningfull stuff omitted
// ...
Point3D _pos;
};
我正在使用地图来存储分子 j 的索引以及与分子 i 的距离。
#define MAX_NEIGHBOUR 4
struct Neighbours {
std::map<double,size_t> _n;
double _limit;
Neighbours() : _limit(std::numeric_limits<double>::infinity()) {}
double add(double dd, size_t index) {
if (dd <= _limit) { // update neighbours only when necessary
_n[dd] = index;
if ( _n.size() > MAX_NEIGHBOUR ) {
auto itn = _n.end();
_n.erase(--itn);
if ( dd < _limit ) _limit = dd;
}
}
}
friend std::ostream & operator<<( std::ostream & out, const Neighbours & nn) {
for ( auto i : nn._n) {
out << i.second << " at " << std::sqrt(i.first) << ", ";
}
return out << std::endl;
}
};
我假设距离矩阵是对称的(除非您使用相同的奇怪拓扑)并将其存储在单个向量中。
struct DistMol {
DistMol(const std::vector<Molecule> & mols) : _rows(mols.size()),
_d(_rows*(_rows-1)/2+1),
_near(_rows),
_mol(mols){}
void evaluate() { // The "matrix" of distances is simmetric
auto id = _d.begin();
for (size_t i=0; i<_rows; i++) {
for (size_t j=i+1; j<_rows; j++) {
*id = squareDistance(_mol[i]._pos,_mol[j]._pos);
_near[i].add(*id,j);
_near[j].add(*id,i);
++id;
}
}
}
double distance(size_t i, size_t j) { // remember that I have only half the matrix
long int dij = i - j;
if ( dij > 0 && i < _rows )
return std::sqrt(_d[j*_rows - j*(j+1)/2 + dij - 1]);
else if ( dij < 0 && j < _rows)
return std::sqrt(_d[i*_rows - i*(i+1)/2 - dij - 1]);
else return 0.0;
}
void showDistances() {
std::cout << std::fixed;
for ( size_t i=0; i<_rows; i++) {
for (size_t j=0; j<_rows; j++) {
std::cout << distance(i,j) << ' ';
}
std::cout << std::endl;
}
}
void showNearest() {
size_t i = 0;
for ( auto n : _near) {
std::cout << i++ << ": " << n;
}
}
size_t _rows;
std::vector<double> _d;
std::vector<Neighbours> _near;
const std::vector<Molecule> & _mol;
};
这是一个使用示例及其输出:
int main() {
std::vector<Molecule> mo {{1.0,0.0,0.5}, {2.0,0.5,1.2}, {4.8,2.3,1.9},
{3.7,3.9,8.7}, {2.1,0.5,1.3}, {5.8,2.5,4.4},
{5.7,4.1,2.3}, {1.7,0.8,6.2}, {0.8,0.3,1.0}};
DistMol dm {mo};
dm.evaluate();
dm.showDistances();
dm.showNearest();
return 0;
}
哪些输出:
0.000000 1.319091 4.657252 9.473120 1.449138 6.670832 6.491533 5.798276 0.616441
1.319091 0.000000 3.401470 8.408329 0.141421 5.355371 5.278257 5.017968 1.232883
4.657252 3.401470 0.000000 7.071775 3.300000 2.700000 2.051828 5.509083 4.561798
9.473120 8.408329 7.071775 0.000000 8.299398 4.985980 6.708204 4.456456 8.981091
1.449138 0.141421 3.300000 8.299398 0.000000 5.224940 5.188449 4.925444 1.349074
6.670832 5.355371 2.700000 4.985980 5.224940 0.000000 2.641969 4.789572 6.434283
6.491533 5.278257 2.051828 6.708204 5.188449 2.641969 0.000000 6.488451 6.335614
5.798276 5.017968 5.509083 4.456456 4.925444 4.789572 6.488451 0.000000 5.300943
0.616441 1.232883 4.561798 8.981091 1.349074 6.434283 6.335614 5.300943 0.000000
0: 8 at 0.616441, 1 at 1.319091, 4 at 1.449138, 2 at 4.657252,
1: 4 at 0.141421, 8 at 1.232883, 0 at 1.319091, 2 at 3.401470,
2: 6 at 2.051828, 5 at 2.700000, 4 at 3.300000, 1 at 3.401470,
3: 7 at 4.456456, 5 at 4.985980, 2 at 7.071775, 4 at 8.299398,
4: 1 at 0.141421, 8 at 1.349074, 0 at 1.449138, 2 at 3.300000,
5: 6 at 2.641969, 2 at 2.700000, 3 at 4.985980, 4 at 5.224940,
6: 2 at 2.051828, 5 at 2.641969, 4 at 5.188449, 1 at 5.278257,
7: 3 at 4.456456, 5 at 4.789572, 4 at 4.925444, 1 at 5.017968,
8: 0 at 0.616441, 1 at 1.232883, 4 at 1.349074, 2 at 4.561798,