Fastest way to sort vectors by angle without actually computing that angle

后端 未结 7 1346
长情又很酷
长情又很酷 2020-12-09 04:19

Many algorithms (e.g. Graham scan) require points or vectors to be sorted by their angle (perhaps as seen from some other point, i.e. using difference vectors). This order i

7条回答
  •  鱼传尺愫
    2020-12-09 04:29

    I know one possible such function, which I will describe here.

    # Input:  dx, dy: coordinates of a (difference) vector.
    # Output: a number from the range [-1 .. 3] (or [0 .. 4] with the comment enabled)
    #         which is monotonic in the angle this vector makes against the x axis.
    def pseudoangle(dx, dy):
        ax = abs(dx)
        ay = abs(dy)
        p = dy/(ax+ay)
        if dx < 0: p = 2 - p
        # elif dy < 0: p = 4 + p
        return p
    

    So why does this work? One thing to note is that scaling all input lengths will not affect the ouput. So the length of the vector (dx, dy) is irrelevant, only its direction matters. Concentrating on the first quadrant, we may for the moment assume dx == 1. Then dy/(1+dy) grows monotonically from zero for dy == 0 to one for infinite dy (i.e. for dx == 0). Now the other quadrants have to be handled as well. If dy is negative, then so is the initial p. So for positive dx we already have a range -1 <= p <= 1 monotonic in the angle. For dx < 0 we change the sign and add two. That gives a range 1 <= p <= 3 for dx < 0, and a range of -1 <= p <= 3 on the whole. If negative numbers are for some reason undesirable, the elif comment line can be included, which will shift the 4th quadrant from -1…0 to 3…4.

    I don't know if the above function has an established name, and who might have published it first. I've gotten it quite a while ago and copied it from one project to the next. I have however found occurrences of this on the web, so I'd consider this snipped public enough for re-use.

    There is a way to obtain the range [0 … 4] (for real angles [0 … 2π]) without introducing a further case distinction:

    # Input:  dx, dy: coordinates of a (difference) vector.
    # Output: a number from the range [0 .. 4] which is monotonic
    #         in the angle this vector makes against the x axis.
    def pseudoangle(dx, dy):
        p = dx/(abs(dx)+abs(dy)) # -1 .. 1 increasing with x
        if dy < 0: return 3 + p  #  2 .. 4 increasing with x
        else:      return 1 - p  #  0 .. 2 decreasing with x
    

提交回复
热议问题