可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
like the title says, I'm trying to write a program that takes a list of (x, y) coordinates, and determines if any 3 points are collinear (lie on a line with the same slope)
I'm getting some error messages. As it stands, I get an "TypeError: 'int' object is not subscriptable" message. If I take out the part where collinearityTest calls on the areCollinear function, I get an "index out of range" error. I'm new to python, and just trying to learn.
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)
ERROR MESSAGE:
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
回答1:
Here's an easier and numerically more robust and stable function to test the collinearity of three points:
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
(Note that it would be best not to hard-code the epsilon, and to make it relative to the length of the vectors.)
回答2:
The Error
The main error is that you are trying to access part of the int
object, but it is not possible. You can reproduce similar error like this:
>>> p1 = 1
>>> p1[1]
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
p1[1]
TypeError: 'int' object is not subscriptable
Other problems
You have several problems with your code, especially two come in mind:
- Divisions (you are not using Python 3.x, so
/
works in a way different than you want, eg. the following is true: 3/2==1
- you should use divisions involving float
s or at least use from __future__ import division
),
- Three levels of loops - this is bad idea because of complexity, just use
itertools.combinations
instead.
Framework for improvements
You should just do something like:
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:
Your code could be much cleaner:
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")
回答4:
So you want to have 3 for loops and each of them are iterating through the same list of points. Then why do you have the while loop? Just remove it. It's needless in this case.
Furthermore; pointList[position]
is a 2d tuple, e.g (10,20). And by writing for p1 in pointList[position]
, you are trying to iterate over that tuple. What you want is to iterate over the list. So try for p1 in pointList
instead. I.e., remove the angle brackets to iterate over the list, not the tuple. Therefore; you don't need to keep track of position as well.
So it becomes
for p1 in pointList:
for p2 in pointList:
for p3 in pointList:
#do something with p1 p2 p3
Tip: You might also consider having areCollinear
function return a boolean value instead of printing something. Doesn't really change the functionality but it's a better practice, as it makes your function reusable somewhere else later on.
回答5:
I would use itertools.combinations()
, but since you're trying to learn Python, here's your basic problem: you're going a level deeper into pointList
than you need to.
Modified function:
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
will give you each item in pointList
. That's exactly what you want. You could also do this with indexes (pointList[index]
) if you like.
Again, go for itertools.combinations()
.
回答6:
There are some problems with the other answers, such as checking for divide by zero. Here's my solution that uses the "All" Python feature and can check a point list of any length:
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:]])