[英]Intersection of hyperbolae in R based on line equations
我正在嘗試找到一種在 R 中找到 2 個雙曲線交點的方法。 單分支hyperbloae可以用以下等式描述:
或者
其中(xi, yi)
和(xj, yj)
是 2 個焦點 ( i
和j
) 的坐標, r
是雙曲線(x, y)
上給定點與每個焦點之間的距離差。
使用 R 可視化雙曲線的最佳方法似乎是可視化 3D 雙曲面的等高線(當等高線水平 = 0),使用上述等式確定並將其實現到 ZC1C425268E6837A944F1
f1 <- function(xi, yi, xj, yj, x, y, r){
sqrt((xj - x)^2 + (yj - y)^2) - sqrt((xi - x)^2 + (yi - y)^2) - r
}
例如,我們可以構建一個網格並可視化兩條雙曲線的等高線級別 0:
library(tidyverse)
library(sf)
# sample points
tribble(
~name, ~x, ~y,
'a', 25, 25,
'b', 75, 25,
'c', 50, 75,
) %>%
{. ->> sample_points}
# sample hyperbolae
expand.grid(
x = seq(0, 100, length = 100),
y = seq(0, 100, length = 100)
) %>%
as_tibble %>%
mutate(
z1 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 5), # i = point a, j = point b
z2 = f1(xi = 25, yi = 25, xj = 50, yj = 75, x, y, r = 5) # i = point a, j = point c
) %>%
{. ->> hyp_data} %>%
ggplot()+
geom_contour(aes(x, y, z = z1), breaks = 0, colour = 1)+
geom_contour(aes(x, y, z = z2), breaks = 0, colour = 2)+
geom_point(data = sample_points, aes(x, y, color = name), size = 3)+
coord_sf()
目前,我可以通過從兩條geom_contour
線中提取線數據,使用ggplot_build()
,將線坐標轉換為sf
LINESTRING
,並使用st_intersection
找到交點來找到交點:
# extract the data from the ggplot using ggplot_build()
ggplot_build(
hyp_data %>%
ggplot()+
geom_contour(aes(x, y, z = z1), breaks = 0, colour = 1)+
geom_contour(aes(x, y, z = z2), breaks = 0, colour = 2)+
# geom_point(data = sample_points, aes(x, y, color = name), size = 3)+ # we dont need the point data in ggplot_build()
coord_sf()
) %>%
.$data %>% # keep only the data component
map(. %>% select(x, y) %>% as.matrix %>% st_linestring) %>% # keep coordinates, turn into a linestring
{. ->> lines1}
# make the lines an sf object
tibble(a = 1:2) %>%
mutate(
geom = st_sfc(lines1),
) %>%
st_as_sf %>% # then make the whole thing an sf object
# use st_intersection to find the point of intersection
st_intersection %>%
# then keep only the 'point' (exclude the original 'lines')
filter(
st_is(geom, 'POINT')
) %>%
{. ->> intersection_point} %>%
ggplot()+
geom_sf(colour = 'red', size = 5)+
geom_contour(data = hyp_data, aes(x, y, z = z1), breaks = 0, colour = 1)+
geom_contour(data = hyp_data, aes(x, y, z = z2), breaks = 0, colour = 2)+
geom_point(data = sample_points, aes(x, y, colour = name), size = 3)
這種方法的局限性在於它依賴於ggplot
的geom_contour
可視化輪廓線的能力。 當r
的絕對值接近兩點之間的距離(ab = 50 之間的距離)時,雙曲線變窄並最終變成一條“在”點后面的直線。 這里geom_contour
無法構建等高線,因此沒有創建線數據,所以找不到交點。 看看下面的 plot 應該有 6 行時如何只有 5 行; z6
沒有 plot 並導致警告消息:
expand.grid(
x = seq(0, 100, length = 100),
y = seq(0, 100, length = 100)
) %>%
as_tibble %>%
mutate(
# z = f1(x, y, nr)
z1 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 5),
z2 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 15),
z3 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 25),
z4 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 35),
z5 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 45),
z6 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 50),
) %>%
ggplot()+
geom_contour(aes(x, y, z = z1), breaks = 0, colour = 1)+
geom_contour(aes(x, y, z = z2), breaks = 0, colour = 2)+
geom_contour(aes(x, y, z = z3), breaks = 0, colour = 3)+
geom_contour(aes(x, y, z = z4), breaks = 0, colour = 4)+
geom_contour(aes(x, y, z = z5), breaks = 0, colour = 5)+
geom_contour(aes(x, y, z = z6), breaks = 0, colour = 6)+
geom_point(data = sample_points, aes(x, y, color = name), size = 3)+
coord_sf()
# Warning messages:
# 1: stat_contour(): Zero contours were generated
# 2: In min(x) : no non-missing arguments to min; returning Inf
# 3: In max(x) : no non-missing arguments to max; returning -Inf
我已經開發了在無法生成等高線時創建這些直線的方法,但我希望能夠使用“數學/代數”方法來找到兩條線的交點,而不是依賴於R 中的空間/映射/計數方法。
我已經探索了使用諸如uniroot()
和solve()
之類的函數的選項,但取得的成功有限,可能是因為我對基礎數學的理解有限和/或描述雙曲線的方程同時具有x
和y
項?
我目前的想法涉及編寫一對等式,右側相等為 0(為不正確的數學語言道歉?):
(或三個方程):
其中, (xi, yi)
, (xj, yj)
和(xk, yk)
是三個焦點 ( i
, j
& k
) 的坐標, r
是距離差。 xi, yi, xj, yj, xk, yk
可以代入上例中點a, b, c
的坐標
然后,我認為應該可以使用與此處描述的類似過程https://www.wikihow.com/Algebraically-Find-the-Intersection-of-Two-Lines來求解方程,但我不知道這如何轉化為 R 代碼和/或現有 R 函數。
其目的是根據接收天線 (foci) 接收到的傳輸的到達時間差 (TDOA) 和雙曲線定位原理以及三邊測量/多邊測量(類似於GPS 的工作原理)。
我將處理成千上萬對雙曲線,所以理想情況下,這個過程相對較快,並且可以由普通計算機管理。 此外,在三個以上站點接收到相同傳輸的情況下(例如,'使用 GPS 時的衛星數量)。
我將非常感謝任何人可以提供的任何想法或幫助,因為我一直在思考並努力解決這個問題已經有一段時間了。 謝謝你。
更新:
對於在家玩的任何人, nleqslv::nleqslv()
function 可用於求解聯立方程(非線性方程求解器; nl-eq-slv()
) 。
查看各種示例:
基本上,您為nleqslv()
提供一些起始值* x
(來自?nleqslv()
:對 root 的初始猜測)和一個包含要求解的方程的 function fn
。
對於這個例子,我們在問題中指定的f1()
function 計算x
和y
的每個值的z
值,是每個雙曲線的方程,forms 是要求解的方程。
f1 <- function(xi, yi, xj, yj, x, y, r){
sqrt((xj - x)^2 + (yj - y)^2) - sqrt((xi - x)^2 + (yi - y)^2) - r
}
使用nleqslv()
時,這會轉化為類似的內容:
nleqslv::nleqslv(c(x1_start, x2_start), function(x){
z <- numeric()
z[1] = sqrt((xj - x[1])^2 + (yj - x[2])^2) - sqrt((xi - x[1])^2 + (yi - x[2])^2) - r_ij
z[2] = sqrt((xk - x[1])^2 + (yk - x[2])^2) - sqrt((xi - x[1])^2 + (yi - x[2])^2) - r_ik
z
})
其中xi
、 yi
等是點i
的x
和y
坐標, r_ij
是交點與點i
和j
之間的距離差。 將原方程中的x
替換為x[1]
,將y
替換為x[2]
; 我的理解是,生成的向量(有點令人困惑,也稱為x
)將有 2 個分量,它們是x[1]
,function fn
中的x[2]
,或基於f1()
的原始方程中的x
, y
) f1()
. x1_start
和x2_start
是x[1]
( x
) 和x[2]
( y
) 的起始值。
從問題中的sample_points
中,我們分別調用點a, b, c
、 i, j, k
。 然后,代入xi, yi, xj, yj, xk, yk, d_ij, d_ik
的值和c(25, 25)
的一些起始值*,我們得到如下所示的結果:
library(nleqslv)
nleqslv::nleqslv(c(25, 25), function(x){
z <- numeric()
z[1] = sqrt((75 - x[1])^2 + (25 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z[2] = sqrt((50 - x[1])^2 + (75 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z
}) %>%
{. ->> result_list}
nleqslv()
生成一個包含各種值和消息的列表,解釋方程是如何求解的(有關詳細信息,請參閱?nleqslv()
),但名為x
的列表的第一個組件包含長度為 2 的向量中的解。
result_list[[1]]
# [1] 46.95886 42.22943
第一個元素是我們對x[1]
的值(原始方程中的x
),第二個元素是x[2]
( y
)。
正如我們所看到的,這個點與我們原來的雙曲線的交點相匹配:
# extract the values of x and y from result_list for plotting
px <- result_list[[1]][1]
py <- result_list[[1]][2]
# plot the sample hyperbolae as in the question
expand.grid(
x = seq(0, 100, length = 100),
y = seq(0, 100, length = 100)
) %>%
as_tibble %>%
mutate(
# z = f1(x, y, nr)
z1 = f1(xi = 25, yi = 25, xj = 75, yj = 25, x, y, r = 5), # i = point a, j = point b
z2 = f1(xi = 25, yi = 25, xj = 50, yj = 75, x, y, r = 5) # i = point a, j = point c
) %>%
{. ->> hyp_data} %>%
ggplot()+
geom_contour(aes(x, y, z = z1), breaks = 0, colour = 1)+
geom_contour(aes(x, y, z = z2), breaks = 0, colour = 2)+
geom_point(data = sample_points, aes(x, y, color = name), size = 3)+
coord_sf()+
# and add in the newfound point
geom_point(aes(x = px, y = py), size = 5, shape = 1, colour = 'red')
所以,我們找到了雙曲線的交點——耶!
然而,起始值的選擇仍然存在爭議。 在這種情況下,我根據sample_points
的最小x
和y
值選擇了c(25, 25)
的起始值,因為我“猜測”根(交點)必須在此處附近的某個地方。
請注意,替代起始值可能會返回不正確的根。 幸運的是,似乎有辦法嘗試解決這個問題。
termcd
result_list
它是終止代碼。 據我所見,值1
和2
表示已找到根,但其他值往往表示未找到(正確的)根(盡管向用戶返回了不正確的值)。 有關終止代碼的完整列表,請參見?nleqslv
(我個人不明白大部分單詞。)。
例如,我們使用x
和y
在0
到100
之間的整數組合運行nleqslv()
以查看哪個生成正確的根,以及返回什么termcd
值:
# set up a grid with every combo of x and y between 0 and 100
expand.grid(
x_grid = seq(0, 100, length = 101),
y_grid = seq(0, 100, length = 101)
) %>%
as_tibble %>%
rowwise %>%
# now we run nleqslv(), and extract the x, y, and termcd values
mutate(
px = nleqslv::nleqslv(c(x_grid, y_grid), function(x){
z <- numeric()
z[1] = sqrt((75 - x[1])^2 + (25 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z[2] = sqrt((50 - x[1])^2 + (75 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z
})[[1]][1],
py = nleqslv::nleqslv(c(x_grid, y_grid), function(x){
z <- numeric()
z[1] = sqrt((75 - x[1])^2 + (25 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z[2] = sqrt((50 - x[1])^2 + (75 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z
})[[1]][2],
termcd = nleqslv::nleqslv(c(x_grid, y_grid), function(x){
z <- numeric()
z[1] = sqrt((75 - x[1])^2 + (25 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z[2] = sqrt((50 - x[1])^2 + (75 - x[2])^2) - sqrt((25 - x[1])^2 + (25 - x[2])^2) - 5
z
})[[3]][1],
) %>%
{. ->> grid_results}
# in this instance, we know the root/intersection is roughly (47, 42), so let's check
# which starting values produce the correct result
grid_results %>%
ungroup %>%
# work out which starting values got the correct point
mutate(
correct = ifelse(round(px) == 47 & round(py) == 42, 'Y', 'N')
) %>%
{. ->> grid_results_2} %>%
# then plot, showing colour for correct/incorrect, and include termcd as text
ggplot()+
geom_raster(aes(x_grid, y_grid, fill = factor(correct)))+
coord_sf()+
geom_text(aes(x_grid, y_grid, label = termcd), size = 2)+
geom_point(data = sample_points, aes(x, y, color = name), size = 10)+
geom_text(data = sample_points, aes(x, y, label = name))+
geom_contour(data = hyp_data, aes(x, y, z = z1), breaks = 0, colour = 1, size = 1)+
geom_contour(data = hyp_data, aes(x, y, z = z2), breaks = 0, colour = 2, size = 1)+
labs(
x = 'starting x',
y = 'starting y',
fill = 'Correct answer?',
colour = 'sample_point name'
)
藍色單元格是返回正確根的起始值,紅色單元格返回錯誤根。 數字是termcd
值,藍色區域是 1 或 2,紅色區域是其他值。
所以,只要你選擇的起始值是正確的,你就應該得到正確的答案。 看起來正確答案僅具有1
2
1
2
代碼僅具有正確答案,至少在這種情況下:
# which codes were returned for correct and incorrect answers?
grid_results_2 %>%
group_by(termcd) %>%
distinct(correct) %>%
arrange(termcd)
# # A tibble: 6 x 2
# # Groups: termcd [6]
# termcd correct
# <int> <chr>
# 1 1 Y
# 2 2 Y
# 3 3 N
# 4 4 N
# 5 5 N
# 6 6 N
所以對於這個例子,我猜你可以通過多個選項來選擇起始值,然后只保留終止代碼為1
或2
的值,以確保你得到正確的答案。
我猜這種方法的一個限制是,您必須為每個實例運行 10,000 次起始值迭代 - 當有 10 到 100 的數千(或數百萬)個實例時,這可能會減慢處理時間。 為了緩解這種情況,我使用 25 個起始值而不是 10,000 運行與上面相同的代碼,並且在大多數情況下它返回了正確的答案,並具有相同的終止代碼模式:
目前,這是一個可行的解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.