1

我正在尝试为“朋友之友”算法编写自己的代码。该算法作用于一组 3d 数据点并返回数据集中“光环”的数量。每个光环是距离小于链接长度 b 的点的集合,b 是程序的唯一参数。

算法描述:FOF 算法有一个自由参数,称为链接长度。距离小于或等于连接长度的任何两个粒子称为“朋友”。然后,FOF 组由一组粒子定义,其中该组中的每个粒子通过朋友网络连接到该组中的每个其他粒子。

设置 FOF 组计数器 j=1。

  • 对于尚未与任何组关联的每个粒子 n:

  • 将 n 分配给组 j,为组 j 初始化一个新的成员列表 mlist,其中粒子 n 作为第一个条目,

  • 递归地,对于 mlist 中的每个新粒子 p:

  • 找到距离小于或等于链接长度的 p 的邻居,将尚未分配到组 j 的那些添加到 mlist 中,
  • 记录组 j 的 mlist,设置 j=j+1。

这是我编写算法的尝试。我唯一喜欢这样做的语言是 Python。但是,我需要用 Fortran 编写此代码或使其更快。我真的希望有人能帮助我。

首先,我生成一组应模拟 3 个光环存在的点:

import random
from random import *
import math
from math import *
import numpy
from numpy import *
import time

points = 1000

halos=[0,100.,150.]

x=[]
y=[]
z=[]
id=[]
for i in arange(0,points,1):
   x.append(halos[0]+random())
   y.append(halos[0]+random())
   z.append(halos[0]+random())
   id.append(i)

for i in arange(points,points*2,1):
   x.append(halos[1]+random())
   y.append(halos[1]+random())
   z.append(halos[1]+random())
   id.append(i)

for i in arange(points*2,points*3,1):
   x.append(halos[2]+random())
   y.append(halos[2]+random())
   z.append(halos[2]+random())
   id.append(i)

然后我编写了FOF算法:

  x=array(x)
  y=array(y)
  z=array(z)
  id=array(id)

  t0 = time.time()                         

  id_grp=[]
  groups=zeros((len(x),1)).tolist()
  particles=id
  b=1 # linking length
  while len(particles)>0:
  index = particles[0]
  # remove the particle from the particles list
  particles.remove(index)
  groups[index]=[index]
  print "#N ", index
  dx=x-x[index]
  dy=y-y[index]
  dz=z-z[index]
  dr=sqrt(dx**2.+dy**2.+dz**2.)
  id_to_look = where(dr<b)[0].tolist()
  id_to_look.remove(index)
  nlist = id_to_look
  # remove all the neighbors from the particles list
  for i in nlist:
        if (i in particles):
           particles.remove(i)
  print "--> neighbors", nlist
  groups[index]=groups[index]+nlist
  new_nlist = nlist
  while len(new_nlist)>0:
          index_n = new_nlist[0]
          new_nlist.remove(index_n)
          print "----> neigh", index_n
          dx=x-x[index_n]
          dy=y-y[index_n]
          dz=z-z[index_n]
          dr=sqrt(dx**2.+dy**2.+dz**2.)
          id_to_look = where(dr<b)[0].tolist()
          id_to_look = list(set(id_to_look) & set(particles))
          nlist = id_to_look
          if (len(nlist)==0):
             print "No new neighbors found"
          else:
             groups[index]=groups[index]+nlist
             new_nlist=new_nlist+nlist
             print "------> neigh-neigh", new_nlist
             for k in nlist:
               particles.remove(k)

最后一个是列表中的光环列表groups

这部分代码有点离题,但我认为向您展示它会很好。我基本上是删除所有没有粒子的组,根据粒子的数量对它们进行排序并显示一些属性。

  def select(test,list):
  selected = []
  for item in list:
    if test(item) == True:
      selected.append(item)
  return selected

  groups=select(lambda x: sum(x)>0.,groups)
  # sorting groups
  groups.sort(lambda x,y: cmp(len(x),len(y)))
  groups.reverse()

  print time.time() - t0, "seconds"

  mass=x
  for i in arange(0,len(groups),1):
    total_mass=sum([mass[j] for j in groups[i]])
    x_cm = sum([mass[j]*x[j] for j in groups[i]])/total_mass
    y_cm = sum([mass[j]*y[j] for j in groups[i]])/total_mass
    z_cm = sum([mass[j]*z[j] for j in groups[i]])/total_mass
    dummy_x_cm = [x[j]-x_cm for j in groups[i]]
    dummy_y_cm = [y[j]-y_cm for j in groups[i]]
    dummy_z_cm = [z[j]-z_cm for j in groups[i]]
    dummy_x_cm = array(dummy_x_cm)
    dummy_y_cm = array(dummy_y_cm)
    dummy_z_cm = array(dummy_z_cm)
    dr = max(sqrt(dummy_x_cm**2.+dummy_y_cm**2.+dummy_z_cm**2.))
    dummy_x_cm = max(dummy_x_cm)
    dummy_y_cm = max(dummy_y_cm)
    dummy_z_cm = max(dummy_z_cm)
    print i, len(groups[i]), x_cm, y_cm, z_cm,dummy_x_cm,dummy_y_cm,dummy_z_cm
4

3 回答 3

5

我认为,如果希望生成的代码比您当前的实现更快,那么开始学习 Fortran 是不明智的。最终可能是这样,但我认为最好建议您在考虑用另一种语言(尤其是外语)实现之前尽可能快地实现 Python。

我编写了 Fortran,我个人认为它的性能在 Python 上很糟糕,但是了解这些事情的人提供了令人信服的论据,如果精心设计,Python+SciPy+Numpy 可以在许多科学/工程的计算内核中与 Fortran 相媲美程式。不要忘记,在您计算机上的所有内核都在红热运行之前,您还没有优化您的 Python。

所以:

第一 - 在 Python 中获得一个有效的实现。

第二 - 使您的实施尽可能快。

IF(大写字母,因为它是一个很大的“如果”)代码仍然不够快,将其翻译成编译语言的成本/收益是有利的,然后考虑将其翻译成哪种编译语言。如果你在一个广泛使用 Fortran 的领域,那么一定要学习 Fortran,但它是一种小众语言,学习 C++ 或它的一个亲戚可能对你的职业更有好处。

编辑(太长,无法放入评论框中)

为什么在你的问题上误导我们?你说你唯一熟悉的语言是 Python,现在你说你知道 Fortran。我想你一定对此感到不舒服。而且,从您的评论看来,您真正需要的帮助似乎是让您的 Python 实现更快;杂耍鲍勃提供了一些建议。考虑到这一点,然后并行化。

于 2012-02-22T09:42:17.633 回答
0

指向更有效算法的指针。如果我没记错的话,您正在将一个点与其他所有点进行比较,以查看是否有比链接长度更近的点。对于大量的点,有更快的方法可以找到近邻——空间索引和 KD 树,但毫无疑问还有其他方法对你有用。

于 2012-02-22T09:55:55.283 回答
0

如果您有现代显卡,您可以使用PyOpenCL在 Python 代码中并行处理数百个处理器(取决于您的显卡) 。

您可以调查查看算法 FoF 是否在此voidfinder F90 代码中实现

您可以将距离定义为平方距离以避免使用 sqrt() 并使用 x*x 而不是 x**2...

于 2012-07-23T23:29:16.237 回答