Cara komputasi yang stabil secara numerik antara vektor

14

Saat menerapkan rumus klasik untuk sudut antara dua vektor:

α=arccosv1v2v1v2

orang menemukan bahwa, untuk sudut yang sangat kecil / akut, ada kehilangan presisi dan hasilnya tidak akurat. Seperti yang dijelaskan dalam jawaban Stack Overflow ini , salah satu solusinya adalah menggunakan arctangent sebagai gantinya:

α=arctan2(v1×v2,v1v2)

Dan ini memang memberikan hasil yang lebih baik. Namun, saya bertanya-tanya apakah ini akan memberikan hasil yang buruk untuk sudut yang sangat dekat dengan π/2 . Apakah ini masalahnya? Jika demikian, apakah ada rumus untuk menghitung sudut secara akurat tanpa memeriksa toleransi di dalam ifcabang?

astrojuanlu
sumber
1
Ini akan tergantung pada implementasi dari fungsi tangen invers dua parameter. Versi lambat dan stabil beralih secara kondisional antara bekerja dengan x / y dan y / x untuk mempertahankan presisi, sementara yang cepat hanya memasukkan hal-hal di kuadran kanan dan dengan demikian tidak ada yang lebih tepat daripada versi satu parameter.
origimbo
Anda harus mendefinisikan "kehilangan presisi": misalkan jawaban yang tepat adalah α dan Anda mendapatkan α+Δ . Apakah Anda memerlukan Δα atau Δπ sudah cukup?
Stefano M
Dalam hal ini, jawaban yang benar adalah dan saya mendapat , keduanya . αα1081
astrojuanlu

Jawaban:

18

( Saya telah menguji pendekatan ini sebelumnya, dan saya ingat itu berhasil dengan benar, tetapi saya belum mengujinya secara khusus untuk pertanyaan ini. )

Sejauh yang saya tahu, keduanya dan dapat menderita pembatalan besar jika hampir paralel / tegak lurus — atan2 tidak dapat memberikan akurasi yang baik jika salah satu input dimatikan.v1×v2v1v2

Mulailah dengan merumuskan kembali masalah sebagai menemukan sudut segitiga dengan panjang sisi,dan(Ini semua dihitung secara akurat dalam aritmatika floating point). Ada varian rumus Heron yang terkenal karena Kahan ( Area salah hitung dan Sudut Segitiga yang Seperti Jarum ), yang memungkinkan Anda menghitung luas dan sudut (antara dan ) dari sebuah segitiga yang ditentukan oleh panjang sisinya, dan melakukannya secara stabil secara numerik. Karena pengurangan subproblem ini juga akurat, pendekatan ini harus bekerja untuk input yang sewenang-wenang.a=|v1|b=|v2|c=|v1v2|ab

Mengutip dari makalah itu (lihat hal.3), dengan asumsi , Semua kurung di sini ditempatkan dengan hati-hati, dan mereka peduli; jika Anda menemukan diri Anda mengambil akar kuadrat dari angka negatif, panjang sisi input bukan panjang sisi segitiga.ab

μ={c(ab),if bc0,b(ac),if c>b0,invalid triangle,otherwise
angle=2arctan(((ab)+c)μ(a+(b+c))((ac)+b))

Ada penjelasan tentang bagaimana ini bekerja, termasuk contoh nilai yang gagal rumus lainnya, dalam makalah Kahan. Formula pertama Anda untuk adalah di halaman 4.αC

Alasan utama saya menyarankan rumus Kahan's Heron adalah karena itu membuat primitif yang sangat bagus — banyak pertanyaan geometri planar yang berpotensi rumit dapat direduksi menjadi menemukan area / sudut segitiga sembarang, jadi jika Anda dapat mengurangi masalah Anda menjadi itu, ada formula stabil yang bagus untuk itu, dan tidak perlu untuk membuat sesuatu sendiri.

Sunting Mengikuti komentar Stefano, saya membuat plot kesalahan relatif untuk , ( kode ). Dua baris tersebut adalah kesalahan relatif untuk dan , sepanjang sumbu horizontal. Tampaknya itu berfungsi. v1=(1,0)v2=(cosθ,sinθ)θ=ϵθ=π/2ϵϵmasukkan deskripsi gambar di sini

Kirill
sumber
Terima kasih atas tautan dan jawabannya! Sayangnya formula kedua yang saya tulis tidak muncul di artikel. Di sisi lain, metode ini bisa sedikit rumit, karena memerlukan proyeksi dalam 2D.
astrojuanlu
2
@astrojuanlu Tidak ada proyeksi untuk 2d di sini: apa pun dua vektor 3d itu, mereka mendefinisikan satu segitiga (planar) di antara mereka — Anda hanya perlu tahu panjang sisinya.
Kirill
Anda benar, komentar saya tidak masuk akal. Saya berpikir dalam koordinat bukannya panjang. Terima kasih lagi!
astrojuanlu
2
@astrojuanlu Satu hal lagi yang ingin saya catat: tampaknya ada bukti formal bahwa rumus area akurat dalam Cara Menghitung Luas Segitiga: Kunjungan Resmi , Sylvie Boldo , menggunakan Flocq.
Kirill
Jawaban yang sangat bagus, tetapi saya berpendapat bahwa Anda selalu dapat menghitung secara akurat dalam aritmatika floating point. Bahkan jika maka pembatalan katastropik terjadi dalam menghitung komponen . cc<ϵmin(a,b)(v1v2)
Stefano M
7

Jawaban efisien untuk pertanyaan ini, tidak terlalu mengejutkan, dalam catatan lain oleh Velvel Kahan :

α=2arctan(v1v1+v2v2,v1v1v2v2)

di mana saya menggunakan sebagai sudut yang dibuat oleh dengan sumbu horizontal. (Anda mungkin harus membalik urutan argumen dalam beberapa bahasa.)arctan(x,y)(x,y)

(Saya memberikan demonstrasi Mathematica dari formula Kahan di sini .)

JM
sumber
Maksudmu ? arctan2
astrojuanlu
1
Saya terbiasa menggambarkan arctangent dua argumen sebagai , ya. Dalam bahasa seperti FORTRAN, padanannya adalah . arctan(x,y)ATAN2(Y, X)
JM