簡體   English   中英

基於直線方程的 R 雙曲線的交點

[英]Intersection of hyperbolae in R based on line equations

我正在嘗試找到一種在 R 中找到 2 個雙曲線交點的方法。 單分支hyperbloae可以用以下等式描述:

方程圖像在這里

或者

在此處輸入圖像描述

其中(xi, yi)(xj, yj)是 2 個焦點 ( ij ) 的坐標, 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)

在此處輸入圖像描述

這種方法的局限性在於它依賴於ggplotgeom_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

在此處輸入圖像描述

z6理論上應該用下面的虛線表示: 在此處輸入圖像描述

我已經開發了在無法生成等高線時創建這些直線的方法,但我希望能夠使用“數學/代數”方法來找到兩條線的交點,而不是依賴於R 中的空間/映射/計數方法。

我已經探索了使用諸如uniroot()solve()之類的函數的選項,但取得的成功有限,可能是因為我對基礎數學的理解有限和/或描述雙曲線的方程同時具有xy項?


我目前的想法涉及編寫一對等式,右側相等為 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 計算xy的每個值的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
})

其中xiyi等是點ixy坐標, r_ij是交點與點ij之間的距離差。 將原方程中的x替換為x[1] ,將y替換為x[2] 我的理解是,生成的向量(有點令人困惑,也稱為x )將有 2 個分量,它們是x[1] ,function fn中的x[2] ,或基於f1()的原始方程中的xy ) f1() . x1_startx2_startx[1] ( x ) 和x[2] ( y ) 的起始值。

從問題中的sample_points中,我們分別調用點a, b, ci, 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的最小xy值選擇了c(25, 25)的起始值,因為我“猜測”根(交點)必須在此處附近的某個地方。

請注意,替代起始值可能會返回不正確的根。 幸運的是,似乎有辦法嘗試解決這個問題。

termcd result_list它是終止代碼。 據我所見,值12表示已找到根,但其他值往往表示未找到(正確的)根(盡管向用戶返回了不正確的值)。 有關終止代碼的完整列表,請參見?nleqslv (我個人不明白大部分單詞。)。

例如,我們使用xy0100之間的整數組合運行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

所以對於這個例子,我猜你可以通過多個選項來選擇起始值,然后只保留終止代碼為12的值,以確保你得到正確的答案。

我猜這種方法的一個限制是,您必須為每個實例運行 10,000 次起始值迭代 - 當有 10 到 100 的數千(或數百萬)個實例時,這可能會減慢處理時間。 為了緩解這種情況,我使用 25 個起始值而不是 10,000 運行與上面相同的代碼,並且在大多數情況下它返回了正確的答案,並具有相同的終止代碼模式: 在此處輸入圖像描述

目前,這是一個可行的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM