(Python) 确定 (x, y) 坐标是不是共线的脚本 - 出现一些错误
Posted
技术标签:
【中文标题】(Python) 确定 (x, y) 坐标是不是共线的脚本 - 出现一些错误【英文标题】:(Python) Script to determine if (x, y) coordinates are colinear - getting some errors(Python) 确定 (x, y) 坐标是否共线的脚本 - 出现一些错误 【发布时间】:2012-03-25 08:59:23 【问题描述】:正如标题所说,我正在尝试编写一个程序,该程序采用 (x, y) 坐标列表,并确定是否有 3 个点共线(位于具有相同斜率的直线上)
我收到一些错误消息。就目前而言,我收到“TypeError:'int' object is not subscriptable”消息。如果我取出 collinearityTest 在 areCollinear 函数上调用的部分,我会收到“索引超出范围”错误。我是python的新手,只是想学习。
def areCollinear(p1, p2, p3):
slope1 = (p2[1] - p1[1]) / (p2[0] - p1[0])
slope2 = (p3[1] - p2[1]) / (p3[0] - p2[0])
if slope1 == slope2:
print "Points are colinear"
else:
print "Points are NOT colinear, what's the matter with you?"
def collinearityTest(pointList):
position = 0
while position >=0 and position < len(pointList):
for p1 in pointList[position]:
position = position + 1
for p2 in pointList[position]:
position = position + 1
for p3 in pointList[position]:
position = position + 1
areCollinear(p1, p2, p3)
pointList = [(10, 20), (55, 18), (10, -45.5), (90, 34), (-34, -67), (10, 99)]
collinearityTest(pointList)
错误信息:
Traceback (most recent call last):
File "C:\Program Files (x86)\Wing IDE 101 4.1\src\debug\tserver\_sandbox.py", line 23, in <module>
File "C:\Program Files (x86)\Wing IDE 101 4.1\src\debug\tserver\_sandbox.py", line 19, in collinearityTest
File "C:\Program Files (x86)\Wing IDE 101 4.1\src\debug\tserver\_sandbox.py", line 2, in areCollinear
if __name__ == '__main__':
TypeError: 'int' object is not subscriptable
【问题讨论】:
如果您发布给您错误的代码会有所帮助。编辑您的问题以包含它。 请发布您的代码。没有它,你的问题基本上是不可能回答的。 告诉我这是可怕的代码并没有真正推动这一点。千感谢您的意见 请发布完整的错误。我们无法猜测(好吧,我们可能会费些力气)生成它的行。 在您的代码和几个答案中,比较由每组三个连续点形成的两条线的浮点斜率是否相等。通常,由于浮点计算的完成方式,这不太可能起作用。您需要使用不同的方法(如@Sven 建议的方法),或者将两个斜率之间差异的绝对值与某个几乎为零的小浮点值进行比较,例如1e-12
。
【参考方案1】:
这里有一个更简单、在数值上更稳健、更稳定的函数来测试三点的共线性:
def collinear(p0, p1, p2):
x1, y1 = p1[0] - p0[0], p1[1] - p0[1]
x2, y2 = p2[0] - p0[0], p2[1] - p0[1]
return abs(x1 * y2 - x2 * y1) < 1e-12
(请注意,最好不要对 epsilon 进行硬编码,并使其与向量的长度相关。)
【讨论】:
OP 注意:这是两个二维向量之间的叉积的大小。如果该值为零,则这些点是共线的。 @Darthfett:有不同的方式来看待这个问题。它也可以解释为两个向量的行列式,因为在两个空间维度中定义 cross product 是相当不寻常的。解释这一点的另一种方法是简单地将原始帖子中使用的身份乘以出现的分母。 它也与两个向量之间的三角形面积成正比(面积为零的三角形是两条平行线源自同一点)。我只是想向 OP 解释一下它为什么/如何工作。由于上下文是“点”,因此使用叉积可以扩展到 3D 点。 应该是abs(x1 * y2 - x2 * y1) < eps
但是如果它们是 3d 点呢?你的代码不起作用。【参考方案2】:
错误
主要错误是您试图访问int
对象的一部分,但这是不可能的。您可以像这样重现类似的错误:
>>> p1 = 1
>>> p1[1]
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
p1[1]
TypeError: 'int' object is not subscriptable
其他问题
您的代码有几个问题,尤其是两个问题:
-
Divisions(您没有使用 Python 3.x,因此
/
的工作方式与您想要的不同,例如。以下是正确的:3/2==1
- 您应该使用涉及 float
s 或至少使用from __future__ import division
),
三级循环 - 由于复杂性,这是个坏主意,只需改用 itertools.combinations
。
改进框架
你应该这样做:
import itertools
for x, y, z in itertools.combinations(pointList, 3):
# Check if x, y and z lie on the same line,
# where x, y and z are tuples with two elements each.
# And remember to use floats in divisions
# (eg. `slope1 = float(p2[1] - p1[1]) / (p2[0] - p1[0])`)
pass
【讨论】:
【参考方案3】:您的代码可能会更简洁:
import itertools
def arecolinear(points):
xdiff1 = float(points[1][0] - points[0][0])
ydiff1 = float(points[1][1] - points[0][1])
xdiff2 = float(points[2][0] - points[1][0])
ydiff2 = float(points[2][1] - points[1][1])
# infinite slope?
if xdiff1 == 0 or xdiff2 == 0:
return xdiff1 == xdiff2
elif ydiff1/xdiff1 == ydiff2/xdiff2:
return True
else:
return False
pointlist = [(10, 20), (55, 18), (10, -45.5), (90, 34), (-34, -67), (10, 99)]
for points in itertools.combinations(pointlist, 3):
if arecolinear(points):
print("Points are colinear")
else:
print("Points are NOT colinear")
【讨论】:
这里的关键是他们的python库itertools
已经具有在列表中创建元素组合的功能:docs.python.org/library/itertools.html#itertools.combinations
@nightcracker:你知道1.8/1
不等于Python 2.x 中的18/10
,在这个例子中使用的很清楚吗?
@Tadeck:OP 显然使用了 Python 2.x,但此答案中的代码并非特定于 2.x 或 3.x。
@Tadeck:是的,我知道,我会在其中添加一个float
以防止该错误。我只是懒惰:P
@SvenMarnach:当我查看答案时,代码显然是特定于 2.x 的(视频:print
语句而不是函数)。但实际上它现在可能不是特定于 2.x 的。【参考方案4】:
因此,您希望有 3 个 for 循环,并且每个循环都遍历相同的点列表。那你为什么有while循环?只需将其删除。在这种情况下是不必要的。
此外; pointList[position]
是一个二维元组,例如 (10,20)。通过编写for p1 in pointList[position]
,您正在尝试迭代该元组。您想要的是遍历列表。所以试试for p1 in pointList
。即,删除尖括号以迭代列表,而不是元组。所以;您也不需要跟踪位置。
这样就变成了
for p1 in pointList:
for p2 in pointList:
for p3 in pointList:
#do something with p1 p2 p3
提示:您也可以考虑让areCollinear
函数返回一个布尔值而不是打印一些东西。并没有真正改变功能,但这是一种更好的做法,因为它使您的函数可以在以后的其他地方重用。
【讨论】:
【参考方案5】:我会使用itertools.combinations()
,但由于您正在尝试学习 Python,所以这是您的基本问题:您对pointList
的了解比您需要的更深。
修改功能:
def collinearityTest(pointList):
for p1 in pointList:
for p2 in pointList:
if p2 is p1:
continue
for p3 in pointList:
if p3 is p2 or p3 is p1:
continue
areCollinear(p1, p2, p3)
for p1 in pointList
将为您提供pointList
中的每个项目。这正是你想要的。如果您愿意,也可以使用索引 (pointList[index]
) 来执行此操作。
再一次,去itertools.combinations()
。
【讨论】:
感谢您的回复。我对“如果 p2 是 p1”和“如果 p3 是 p2 或 p3 是 p1”感到困惑,我以前从未见过这样的事情。这是做什么的? 显然你不想比较两个相同的点是否共线。由于您对同一个列表进行了 3 次迭代,因此这可以避免出现p1
和 p2
、p2
和 p3
或 p1
和 p3
都是 @ 的相同元素的任何情况987654334@。 itertools 的组合具有相同的结果(pointList
的所有可能不同组合)。【参考方案6】:
其他答案存在一些问题,例如检查除以零。这是我的解决方案,它使用“全部”Python 功能并且可以检查任意长度的点列表:
def collinear(Points):
'''Accepts [(x1, y1), (x2, y2), ...] and returns true if the
points are on the same line.'''
ERR=1.0e-12
if len(Points)<3:
return True
x1, y1 = Points[0]
x2, y2 = Points[1]
if x2==x1:
raise Exception("points are not a function")
m=(y2-y1)/(x2-x1)
return all([abs(m*(xi-x1)-yi+y1)<ERR for xi,yi in Points[2:]])
【讨论】:
【参考方案7】:您可以使用rank of a matrix(即二维数组)来确定其列(解释为坐标列表)是否共线:当且仅当所有点共线时,其等级为一。
这在所有维度上都是正确的,并且具有任意数量的列(即任意数量的点)。
因此,使用numpy.linalg.matrix_rank
的优雅方法是:
from numpy.linalg import matrix_rank
def are_collinear(coords, tol=None):
return matrix_rank(coords, tol=tol)==1
在三点的情况下使用:are_collinear([p0, p1, p2])
。但是你可以插入一个带有任意数量点的迭代,而不仅仅是三个,例如你的pointList
列表。
【讨论】:
以上是关于(Python) 确定 (x, y) 坐标是不是共线的脚本 - 出现一些错误的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Dart 将 x、y 坐标编码为 python ecdsa VerifyingKey 格式