c ++中的成对距离计算

时间:2013-10-08 16:36:03

标签: c++ algorithm euclidean-distance energy

我正在计算c ++中大量(~1e5)粒子的势能。为了做到这一点,我正在运行一个双循环,我在其中计算成对距离,并从这些距离计算系统的总势能。下面是代码的相关部分(它不是复制/粘贴准备好的,因为需要定义数据,有些东西不在上下文中;方法仍然有效,这就是我在这里要展示的内容) :

int colstart = 2;
int colend = 4;
double PE = 0;
double p_mass = 8.721e9 * 1.989e30; // mass of sim particle in kg
double mpc_to_m = 3.08567758e22; // meters per mpc
double G = 6.67384e-11; // grav. constant in mks units

// Calculating PE
for(int i = 0; i < data.size()-1; i++) // -1 is for empty line at the end of every file
  {
    //cout << i << " " << data[i].size() << endl;
    for(int j = 0; j < i; j++)
     {

       double x_i = (double)atof(data[i][2].c_str());
       double y_i = (double)atof(data[i][3].c_str());
       double z_i = (double)atof(data[i][4].c_str());

       double x_j = (double)atof(data[j][2].c_str());
       double y_j = (double)atof(data[j][3].c_str());
       double z_j = (double)atof(data[j][4].c_str());

       double dist_betw = sqrt(pow((x_i-x_j),2) + pow(y_i-y_j,2) + pow(z_i-z_j,2)) * mpc_to_m;

       PE += (-1 * G * pow(p_mass,2)) / (dist_betw);  

     }
  }

有更快的方法来进行此类计算吗?我愿意接受有关近似的建议,即如果它将总势能返回到大约1%左右。

谢谢!

7 个答案:

答案 0 :(得分:5)

一些潜在的微观受害者:

  • 乘法可能比使用pow()平方距离更快;
  • 公共因子-1 * G * pow(p_mass,2) / mpc_to_m可以保持不变;
  • 总结1.0 / dist_betw,然后乘以最后的公因子可能会稍快一些。
  • 您可能(或可能不)能够准确地近似平方根,比sqrt()更快。有很多approximations可供尝试。
  • 将每个坐标转换为double一次,将值存储在另一个数组中,而不是存储在内循环的每次迭代中。

从算法上讲,您可以丢弃距离当前点太远的粒子,从而对能量做出重大贡献。一个简单的修改可以检查平方距离之前昂贵的平方根,如果它太大则继续前进。您可以通过将粒子存储在空间感知的数据结构(如Octree或仅仅是一个简单的网格)中来进一步改进,这样您甚至不需要查看远点;但如果粒子频繁移动,那可能就不切实际了。

答案 1 :(得分:4)

从字符串转换为double是很昂贵的,将这些转移到循环外部并将它们缓存到单独的datacache [],

typedef struct { double x, y, z; } position;
position datacache[data.size()];
for(int i = 0; i < data.size()-1; i++)
{
   datacache[i].x = (double)atof(data[i][2].c_str());
   datacache[i].y = (double)atof(data[i][3].c_str());
   datacache[i].z = (double)atof(data[i][4].c_str());
}

然后在你的循环中使用datacache []。x,.y,.z。

你可以使用float而不是double,因为你愿意接近1%(所以失去从double得到的精度的额外数字仍然在精度的8-9位数内)。

另一项效率改进 - 你也可以考虑使用定点整数算法(对于距离),你决定值的范围,而不是像浮点数/双精度那样使用显式小数点存储,缩放你的定点数通过隐含值(可以计算距离计算。

算法优化 - 将您的3D空间分成区域,计算区域上的聚合,然后聚合来自区域的效果。

答案 2 :(得分:4)

  1. 预先计算循环外的所有可能性
  2. 将数组元素的转换从循环移到avoud以多次转换任何值。
  3. 守则:

    int colstart = 2;
    int colend = 4;
    double PE = 0;
    double p_mass = 8.721e9 * 1.989e30; // mass of sim particle in kg
    double mpc_to_m = 3.08567758e22; // meters per mpc
    double G = 6.67384e-11; // grav. constant in mks units
    double constVal= (-1 * G * pow(p_mass,2)) / mpc_to_m
    
    double values[][3]= ... ;// allocate an array of size  data.size()*3
    for(int i = 0; i < data.size()-1; i++){
        value[i][0]=(double)atof(data[i][2].c_str());
        value[i][1]=(double)atof(data[i][3].c_str());
        value[i][2]=(double)atof(data[i][4].c_str());
    }
    
    // Calculating PE
    for(int i = 0; i < data.size()-1; i++){
         //cout << i << " " << data[i].size() << endl;
         double xi=value[i][0]
         double yi=value[i][1];
         double yi=value[i][2];
         for(int j = 0; j < i; j++){
             double xDiff = xi - value[j][0] ;
             double yDiff = yi - value[j][1] ;
             double zDiff = zi - value[j][2];
             PE += constVal / (xDiff*xDiff + yDiff*yDiff + zDiff*zDiff) ;  
         }
    }
    

    但只有剖析可以显示是否以及增加了多少速度。

答案 3 :(得分:1)

您可以像最近配对问题一样使用分而治之的方法:http://en.m.wikipedia.org/wiki/Closest_pair_of_points_problem。您还可以在O(n log n)中计算delaunay或voronoi图。

答案 4 :(得分:1)

Fast Multipole Method已被用于将看似相似的O(n ^ 2)全对n体模拟问题加速到O(n)。我不熟悉曾经在“十大算法发明”列表中看到的细节,但我认为基本思想是大多数粒子对都是远距离相互作用,它们很弱并且对小的不太敏感位置的变化,意味着它们可以通过聚类很好地近似(我不确定这是怎么做的)而不会失去太多的准确性。您可以将相似的技术应用于您的问题。

答案 5 :(得分:1)

你应该做的一件事,至少是移动线

   double x_i = (double)atof(data[i][2].c_str());
   double y_i = (double)atof(data[i][3].c_str());
   double z_i = (double)atof(data[i][4].c_str());

在内环之外。这些值仅取决于i而不取决于j,因此您绝对不希望每次都重新解析它们。然后有一些微优化可以让它运行得更快一点。最后,如果您使用的是多处理器计算机,则可以使用openMP轻松地将其并行化。代码的半优化和并行化版本可能如下所示:

inline double squared(double x){
  return x * x;
}

double compute_pe(vector<string *> data){
  double PE = 0;
  double p_mass = 8.721e9 * 1.989e30; // mass of sim particle in kg
  double mpc_to_m = 3.08567758e22; // meters per mpc
  double G = 6.67384e-11; // grav. constant in mks units

  double PEarray[data.size()];

  double numerator = (-1 * G * pow(p_mass,2))/ mpc_to_m;


  size_t i,j;

  // Calculating PE
  #pragma omp parallel for private(i, j)
  for(i = 0; i < data.size()-1; i++) // -1 is for empty line at the end of every file
    {
    PEarray[i]=0;
    double x_i = (double)atof(data[i][2].c_str());
    double y_i = (double)atof(data[i][3].c_str());
    double z_i = (double)atof(data[i][4].c_str());

      //cout << i << " " << data[i].size() << endl;
      for(j = 0; j < i; j++)
       {

        double x_j = (double)atof(data[j][2].c_str());
        double y_j = (double)atof(data[j][3].c_str());
        double z_j = (double)atof(data[j][4].c_str());

        double dist_betw = sqrt(squared(x_i-x_j) + squared(y_i-y_j) + squared(z_i-z_j));

        PEarray[i] += numerator / (dist_betw);
       }
    }

  for(i = 0; i < data.size()-1; i++){
    PE += PEarray[i];
  }

  return PE;
}

答案 6 :(得分:1)

在所有先前建议的基础上,一个常见的技巧是预先计算所有向量的平方范数。

鉴于|| xy || ^ 2 = || x || ^ 2 + || y || ^ 2-2 * xy,如果|| x || ^ 2为| x || x |,则可以避免很多无用的乘法一次为所有x计算一次。

这个成对的距离问题因此成为点积问题,这是一个基本的线性代数运算,可以根据您的硬件通过各种优化的库来计算。