Was ist der "richtige" Weg, um Einheitsvektoren zu erhalten?

7

Ich entschuldige mich, wenn ich den Begriff "Einheitsvektor" falsch verstanden oder im Konzept falsch angewendet habe, aber ich denke, dass dies der Begriff für die Überschrift eines Objekts ist, wenn er als [x, y]wann ausgedrückt wird xund yein Wert zwischen -1 und 1 ist. Wenn sich ein Objekt auf einem Computerbildschirm nach Süden bewegen würde, hätte es einen Einheitsvektor von

[0, 1]

...Recht?

Wie auch immer, ich habe dieses Konzept verwendet, um meine Objekte zu bewegen und ihre Bilder zu drehen (falls erforderlich), aber ich denke, die Mathematik, mit der ich den Einheitsvektor bestimme, ist ... suboptimal.

def set_heading(self, goal):
    """Uses a 'goal' (x, y) to set the object's heading.
    Returns list of 0s and 1s for 'straight' headings.
    Diagonal headings are + and/or - math.sqrt(2)/2.
    """
    vals = [a - b for a, b in zip(goal, self.pos)]
    self.heading = [i / abs(i) if i != 0 else 0 for i in vals]
    if 0 not in self.heading:
        self.heading = [i * (sqrt(2)/2) for i in self.heading]

(Wenn Sie mit der ternären Python-Syntax nicht vertraut sind, enthält i / abs(i) if i != 0 else 0die zweite Zeile die Art und Weise, x if Condition else ywie Python im Gegensatz zur üblichen Condition? x : else ySyntax in anderen Sprachen sagt . Grundsätzlich versuche ich nur, das Teilen durch Null zu vermeiden!)

Wie Sie sehen können, sperrt diese Methode das Objekt in acht Richtungen. Wenn in der Überschrift keine 0 vorhanden ist, geht die Methode davon aus, dass wir uns diagonal sqrt(2) / 2bewegen, und multipliziert die Werte damit, um sicherzustellen, dass sich das Objekt nicht schneller bewegt, als es sollte, wenn es sich diagonal bewegt. Auf diese Weise kann ich das Objekt bewegen, indem ich einfach das Ergebnis des Einheitsvektors multipliziert mit seiner Geschwindigkeit zu seinen aktuellen x- und y-Koordinaten addiere.

def move(self):
    """Moves the object by changing self.pos."""
    self.pos = [a + (b * self.speed) for a, b in zip(self.pos, self.heading)]

Ich kann nicht anders, als das Gefühl zu haben, dass die Methode zum Abrufen des Einheitsvektors (wenn das richtig heißt) sophomorisch ist. Zumal es in acht Richtungen verriegelt ist - es ist in Ordnung für das kleine Projekt, mit dem ich gerade arbeite, aber ich bin mir nicht sicher, wie ich einen genaueren Einheitsvektor erhalten soll. Was ist die richtige Methode dafür - und ist sie mehr oder weniger performant als die Verrücktheit, die ich mir unabhängig ausgedacht habe?

Stock
quelle
Ich habe nicht die Zeit, eine vollständig ausgearbeitete Antwort zu schreiben, aber im Grunde genommen sollten Sie sich selbst von den Zielvektoren abziehen, wodurch Sie den Vektor zwischen diesen beiden Punkten erhalten. Sie normalisieren es dann (dh dividieren durch seine Größe), was zu dem gewünschten Einheitsvektor führt. Der Einheitsvektor (oder oft der "normalisierte Vektor") wird nicht durch Werte zwischen 0 und 1 definiert, sondern durch eine Länge von genau 1 Einheit - beide Werte liegen zwischen 0 und 1 und ergeben sich daraus.
Christian
Recht. Entschuldigung, ich glaube, ich dachte, dass das Verständnis in den Ergebnissen der Methode zum Ausdruck kommt. Eine Methode, die eine Diagonale zurückgibt (wie zum Beispiel 'Südosten'), würde [.707, .707] zurückgeben, hier aus Gründen der Vernunft gerundet. B / c Ich tippe nicht die 15+ Ziffern von sqrt (2) / 2 ein. Aber es würde Koordinaten zurückgeben, die 1 Länge von [0, 0] entfernt sind und mit dem Geschwindigkeitsattribut des Objekts multipliziert werden könnten, um es dorthin zu bringen, wo es hingeht
Stick

Antworten:

12

Ein Einheitsvektor hat die Länge 1.

Ein gegebener Vektor kann in einen Einheitsvektor umgewandelt werden, indem er durch seine Größe dividiert wird. (Mit der Ausnahme, dass ein Vektor mit der Länge Null nicht konvertiert werden kann).

Beachten Sie, dass die Größe mit dem Satz von Pythagoras berechnet werden kann

Zum Beispiel, wenn ein Vektor Komponenten hat: ( x, y, z)

magnitude = sqrt( x2+ y2+ z2)

unit vector = ( x / magnitude , y / magnitude, z / magnitude )


Annan wirft einen interessanten Punkt auf, der bei Vektoren mit einer Größe größer als math.sqrt(sys.float_info.max)(ungefähr 1,3e + 154 auf meinem Computer) fehlschlägt, wenn Floats auf normale Weise verwendet werden. In diesem Fall ist eine Problemumgehung, bei der Longs verwendet werden, um die Arbeitswerte zu halten und dann die Quadratwurzel manuell zu finden, funktionsfähig, aber relativ langsam.

def isqrt(n):
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def large_magnitude(x, y, z):
    long_magnitude_squared = pow(long(x), 2) + pow(long(y), 2) + pow(long(z), 2)
    return float(isqrt(long_magnitude_squared))

Die Gutschrift für diese Implementierung der Newtonschen Methode geht an user448810 .

Kelly Thomas
quelle
Ah, das ist schön. Unvermeidliches Follow-up dann - würde ich idealerweise daran arbeiten, den Aufruf sqrt()aus der Logik herauszuholen (und daher nicht mehr ausschließlich mit Einheitsvektoren zu arbeiten) oder ist dies eine akzeptable Verwendung dafür
Stick
2
Da Sie in Python arbeiten, würde ich vorschlagen - verwenden Sie einfach das sqrt und machen Sie sich darüber keine Sorgen.
Amitp
hah, na ja - das ist auch in Ordnung, kann ich fragen, was das bedeuten soll? Ich riskiere eine Vermutung; dass Python langsam ist und dass es kein ultimatives Ergebnis gibt, wenn man versucht, herumzukommen sqrt(). Was in Ordnung ist - ich codiere nicht für
Stick
1
@Stick Da Python eine interpretierte Sprache ist, entsteht ein gewisser Overhead, der in etwa der Anzahl der ausgeführten Befehle entspricht. Intern verwendet Python gut optimierten Maschinencode, sodass der Prozess der Berechnung der Quadratwurzel nicht diesem Aufwand unterliegt.
aaaaaaaaaaaa
1
@Annan Ich habe eine Problemumgehung hinzugefügt, die Größen bis zu sys.float_info.max(ungefähr 1,7e + 308 auf meinem Computer) verarbeitet. Darüber hinaus möchten Sie wahrscheinlich ohnehin einen langbasierten Vektor verwenden.
Kelly Thomas