Python中高效的向量/点类
实现一个既可以在Python2.7+中使用又可以在Python3.x中使用的高效向量/点类(或者更好:是否已经有了)的最佳方法是什么 我发现了,但它们似乎只支持Python3.x。然后是,它使用,但它只是一个3D向量。为具有静态属性(x和y)的向量()使用列表似乎也很奇怪。(有所有这些列表方法。) 目前,我正在使用一个扩展namedtuple的类(如下所示),但它的缺点是无法更改坐标。我认为这可能会成为一个性能问题,当数千个对象移动时,每次都会创建一个新的(向量)元组。(对吗?) 编辑:我做了一些测试,似乎使用Python中高效的向量/点类,python,performance,python-3.x,vector,Python,Performance,Python 3.x,Vector,实现一个既可以在Python2.7+中使用又可以在Python3.x中使用的高效向量/点类(或者更好:是否已经有了)的最佳方法是什么 我发现了,但它们似乎只支持Python3.x。然后是,它使用,但它只是一个3D向量。为具有静态属性(x和y)的向量()使用列表似乎也很奇怪。(有所有这些列表方法。) 目前,我正在使用一个扩展namedtuple的类(如下所示),但它的缺点是无法更改坐标。我认为这可能会成为一个性能问题,当数千个对象移动时,每次都会创建一个新的(向量)元组。(对吗?) 编辑:我做了一
numpy.array
或numpy.ndarray
作为向量太慢了。(例如,获取一个项目几乎需要两倍的时间,更不用说创建一个数组了。我认为它更适合对大量项目进行计算。)
因此,我正在寻找一个轻量级的向量类,它具有固定数量的字段(在我的例子中,就是
x
和y
),可以用于游戏。(如果已经有一个经过良好测试的轮子,我不想再发明它。)是的,有一个向量类:它在事实上的标准模块中。您可以这样创建向量:
>>> v = numpy.array([1, 10, 123])
>>> 2*v
array([ 2, 20, 246])
>>> u = numpy.array([1, 1, 1])
>>> v-u
array([ 0, 9, 122])
NumPy非常丰富,可以让您访问快速数组操作:点积(NumPy.dot()
),norm(NumPy.linalg.norm()
),等等。在线性代数方面,NumPy中的向量类可能是NumPy.matrix
,它是NumPy.ndarray
的一个子类。它本身并不干净,但它使您的代码更干净,因为代数运算是假定的,而不是元素操作
In [77]: a = np.array([1,2])
In [78]: b = np.array([3,3])
In [79]: a*b
Out[79]: array([3, 6])
In [80]: np.dot(a,b)
Out[80]: 9
In [81]: np.outer(a,b)
Out[81]:
array([[3, 3],
[6, 6]])
In [82]: a = np.matrix(a).T
In [83]: b = np.matrix(b)
In [84]: b*a
Out[84]: matrix([[9]])
In [85]: a*b
Out[85]:
matrix([[3, 3],
[6, 6]])
如果要创建自己的,请基于以下其中一项,例如:
class v2d(np.ndarray):
def __abs__(self):
return np.linalg.norm(self)
def dist(self,other):
return np.linalg.norm(self-other)
def dot(self, other):
return np.dot(self, other)
# and so on
在最简单的情况下,只需将ndarray
作为新类查看即可:
In [63]: a = np.array([1,2]).view(v2d)
In [64]: b = np.array([3,3]).view(v2d)
In [65]: a
Out[65]: v2d([1, 2])
In [66]: abs(b)
Out[66]: 4.2426406871192848
In [67]: a - b
Out[67]: v2d([-2, -1])
In [68]: a*b
Out[68]: v2d([3, 6])
In [69]: a*3
Out[69]: v2d([3, 6])
In [70]: a.dist(b)
Out[70]: 2.2360679774997898
In [71]: b.dist(a)
Out[71]: 2.2360679774997898
In [72]: a.dot(b)
Out[72]: 9
这里有更多关于的信息。我也需要一个快速的解决方案,所以我只是将numpy的数组包装到我自己的数组中。您会注意到一些设计决策可以根据自己的需要进行更改(如默认值)。 如果您想使用它:
谢谢您知道一个干净的包装器类,它使用numpy表示二维向量吗?(numpy.array充满了向量不需要的其他方法)有一个子类
np.ndarray
称为np.matrix
。它并不干净,但是如果你有两个matrix
e,A*B
提供了这两个元素的矩阵积(比如,np.dot(A,B)
如果它们是双向量):我想如果你这么担心性能,你使用的语言是错误的(别误会,python很棒,但CPython很慢;如果你不依赖第三方软件包,你可以尝试PyPy)。即使你依赖(一些)第三方软件包,你也可以尝试PyPy。特别是,纯python软件包应该可以工作,一些非纯python软件包已经被移植,或者正在被移植(至少部分).我检查过了,但比你建议的要慢,因为协调访问。它也更一般……我从未见过Python的2D或3D优化向量实现:我想自己实现它是最好的选择。您甚至可以在Python包索引上发布结果。:)你考虑过使用复数吗?它们给你v1+v2,v1-v2,实x*v,v/x,abs(v)和abs(pt1-pt2)。单位向量的乘法是旋转,而a*pt+b是任何仿射变换。如果从复合体中导入exp和log,则会得到角度旋转、向量的反正切或差,以及极轴和矩形之间的转换。因为很难说.real和.imag而不是.x和.y,所以我编写了complex的子类来重命名属性。这使得我的积分更大,我肯定比原始的复杂度要慢。
In [63]: a = np.array([1,2]).view(v2d)
In [64]: b = np.array([3,3]).view(v2d)
In [65]: a
Out[65]: v2d([1, 2])
In [66]: abs(b)
Out[66]: 4.2426406871192848
In [67]: a - b
Out[67]: v2d([-2, -1])
In [68]: a*b
Out[68]: v2d([3, 6])
In [69]: a*3
Out[69]: v2d([3, 6])
In [70]: a.dist(b)
Out[70]: 2.2360679774997898
In [71]: b.dist(a)
Out[71]: 2.2360679774997898
In [72]: a.dot(b)
Out[72]: 9
import numpy as np
class Point(np.ndarray):
"""
n-dimensional point used for locations.
inherits +, -, * (as dot-product)
> p1 = Point([1, 2])
> p2 = Point([4, 5])
> p1 + p2
Point([5, 7])
See ``test()`` for more usage.
"""
def __new__(cls, input_array=(0, 0)):
"""
:param cls:
:param input_array: Defaults to 2d origin
"""
obj = np.asarray(input_array).view(cls)
return obj
@property
def x(self):
return self[0]
@property
def y(self):
return self[1]
@property
def z(self):
"""
:return: 3rd dimension element. 0 if not defined
"""
try:
return self[2]
except IndexError:
return 0
def __eq__(self, other):
return np.array_equal(self, other)
def __ne__(self, other):
return not np.array_equal(self, other)
def __iter__(self):
for x in np.nditer(self):
yield x.item()
def dist(self, other):
"""
Both points must have the same dimensions
:return: Euclidean distance
"""
return np.linalg.norm(self - other)
def test():
v1 = Point([1, 2, 3])
v2 = Point([4, 5, 7])
v3 = Point([4, ])
sum12 = Point([5, 7, 10])
dot12 = Point([4, 10, 21])
# Access
assert v2.x == 4
assert v2.y == 5
assert v2.z == 7
assert v3.z == 0
assert Point().x == 0
assert v2[0] == 4
assert v1[-1] == 3 # Not needed but inherited
assert [x for x in v2] == [4, 5, 7], "Iteration should return all elements"
# Operations
assert v1 + v2 == sum12
assert v1 * v2 == dot12
assert v1.dist(v2) ** 2 == 34
assert v1 != v2
assert v2.size == 3, "v2 should be a 3d point"
print "pass"
if __name__ == "__main__":
test()