有一段时间我现在受到两种物理系统建模设计理念冲突的困扰,我想知道社区为此提出了什么样的解决方案。
对于复杂(呃)模拟,我喜欢为对象创建类的抽象,以及如何使用我想要研究的真实对象来识别类的对象实例,以及对象的某些属性如何表示对象的物理特征。现实生活中的物体 让我们以弹道粒子系统为例:
class Particle(object):
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "x={}\ny={}\nz={}".format(self.x, self.y, self.z)
def apply_lateral_wind(self, dx, dy):
self.x += dx
self.y += dy
如果我用一百万个值初始化它,我可能会这样做:
start_values = np.random.random((int(1e6),3))
particles = [Particle(*i) for i in start_values]
现在,让我们假设我需要对我的所有粒子做一个特定的事情,比如添加一个横向风向量,只为这个特定的操作导致ax,y shift,因为我只有一堆(列表)所有我的粒子,我需要遍历我的所有粒子才能做到这一点,这需要花费这么多时间:
%timeit _ = [p.apply_lateral_wind(0.5, 1.2) for p in particles]
1 loop, best of 3: 551 ms per loop
现在,对此明显更有效的反对明显范式是保持numpy
水平,直接对数组进行数学运算,速度超过10倍:< / p>
%timeit start_values[...,:2] += np.array([0.5,1.2])
10 loops, best of 3: 20.3 ms per loop
我现在的问题是,如果有任何设计模式可以有效地结合这两种方法,那么人们不会失去那么高的效率?我觉得从对象方法和属性的角度来说,个人思考起来真的更容易,在我的脑海中更清晰,对我而言,实际上也是面向对象编程成功的根本原因(或者它用于(物理)建模) 。但缺点是显而易见的。如果这些方法之间有某种优雅的反复可能会爱吗?
答案 0 :(得分:6)
您可以定义一个处理多个粒子的类:
class Particles(object):
def __init__(self, coords):
self.coords = coords
def __repr__(self):
return "Particles(coords={})".format(self.coords)
def apply_lateral_wind(self, dx, dy):
self.coords[:, 0] += dx
self.coords[:, 1] += dy
start_values = np.random.random((int(1e6), 3))
particles = Particles(start_values)
我的系统上的时间显示这实际上比你的numpy版本更快,可能是因为它没有构建一个额外的数组而且不必担心广播:
%timeit particles.apply_lateral_wind(0.5, 1.2)
100 loops, best of 3: 3.17 ms per loop
而使用你的numpy例子给出了
%timeit start_values[..., :2] += np.array([0.5, 1.2])
10 loops, best of 3: 21.1 ms per loop
答案 1 :(得分:2)
Cython extension types可能是一个有趣的选项。 (尽管此链接文档页面中也描述了一些限制。)
我编辑了你的示例代码以进行Cythonize,然后提出了这个:
cdef class Particle(object):
cdef double x, y, z
def __init__(self, double x=0, double y=0, double z=0):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "x={}\ny={}\nz={}".format(self.x, self.y, self.z)
cpdef apply_lateral_wind(self, double dx, double dy):
self.x += dx
self.y += dy
使用这个基本的Cython版本,我在循环超过100万个粒子时获得了以下时间:
>>> %timeit _ = [p.apply_lateral_wind(0.5, 1.2) for p in particles]
10 loops, best of 3: 102 ms per loop
这比普通Python版本快5倍,在同一台机器上耗时532毫秒。话虽如此,它显然仍然比使用裸NumPy阵列慢一个数量级,但我想这是可读性的代价。