Graham Scan для расчета выпуклой оболочки заданного набора точек в R - PullRequest
0 голосов
/ 29 марта 2020

Мне нужно кодировать алгоритм сканирования Грэма, чтобы найти выпуклую оболочку заданного набора точек. Я много пробовал и не могу найти решение.

У меня есть следующий код для функции Грэма

РЕДАКТИРОВАТЬ: добавить комментарии, чтобы помочь понять поток кода

grahamScanConvexHull<-function(points){
  # Points is a dataset with eeach row a point one column x and one column
  # (We can assume that there are not three points collinear)

  # Vertices is a simulated stack were the solution is stored
  vertices <- data.frame(x=double(), y=double())

  flag <- T # Flag used in case there two points with the least y

  # Get the most left down_point and leave others in points
  points  <- quicksort_bottom_top(points, 1, nrow(points))

  # if there are two points with the same lowest y we get the leftmost 
  if(points[1,]$y == points[2,]$y){ 
    if(points[1,]$x < points[2,]$x){
      point_0 <- points[1,]
      points <- points[(2:nrow(points)),]
    }else{
      point_0 <- points[2,]
      points <- points[c(1, 3:nrow(points)),]
    }
  }else{
    point_0 <- points[1,]
    points <- points[(2:nrow(points)),]
    flag  <- F
  }

  # Sort the points related to de most left point
  points <- quicksort_angle(points, 1, nrow(points), point_0)
  # This is used in case that there are two points with the lowest y be put first   
  # in the list of points (it will we the last in other case)
  if(flag){
    first_point <- points[nrow(points),]
    points <- rbind(first_point, points[(1:(nrow(points)-1)),])
  }

  # Push the point with the lowest y (ledtmost) and the point with lest polar 
  #angle
  vertices <- push(vertices, point_0)
  vertices <- push(vertices, points[1,])

  for(i in 1:nrow(points)){
    # Get the top 2 points in the stack as vectors
    next_to_top <- c(next_to_top(vertices)$x,next_to_top(vertices)$y)
    top <- c(top(vertices)$x,top(vertices)$y)

    # Get the oncheck point as a vector
    point_i <- c(points[i,]$x,points[i,]$y)

    while(nrow(vertices) >= 2 & (rightTurn(next_to_top, top, point_i) <= 0)){
      vertices <- pop(vertices)

      # Recalculate the top two points of the stack (as vector) after pop 
      next_to_top <- next_to_top(vertices)
      next_to_top <- c(next_to_top$x, next_to_top$y)

      top <- top(vertices)
      top <- c(top$x, top$y)

    }
    # Push the onCheck point into the stack
    vertices <- push(vertices, points[i,])
  }

  return(vertices)
}

Для управления стеком у меня есть код этой короткой вспомогательной функции

top <- function(stack){
  return(stack[1,])
} 
next_to_top <- function(stack){
  return(stack[2,])
} 
pop <- function(stack){
  return(stack[-1,])
}
push <- function(stack, e){
  if(e$x %in% stack$x & e$y %in% stack$y ){
    if(which(e$x %in% stack$x) == which(e$y %in% stack$y)){
      return(stack)
    }else{
      stack <- rbind(e, stack)
      return(stack)
    }
  }else{
    stack <- rbind(e, stack)
    return(stack)
  }
}

А для против часовой стрелки у меня есть следующая функция:

rightTurn <- function(A, B,  C) {
  val <- (B[1] - A[1]) * (C[2] - A[2]) -
    (B[2] - A[2]) * (C[1] - A[1])
  return(val)
}

Функции сортировки, которые я проверял много раз и, я вполне уверен, что они в порядке (также я использую их в течение длительного времени без каких-либо проблем). Я не знаю, что я делаю неправильно, поэтому любая помощь приветствуется.

РЕДАКТИРОВАТЬ:

Код quicksort_bottom_top ()

quicksort_bottom_top <- function(points, low, high) {
  if (low < high) {
    list_particion <- partition_bottom_top(points, low, high)
    p <- list_particion[['p']]
    points <- list_particion[['points']]
    points <- quicksort_bottom_top(points, low, p - 1)
    points <- quicksort_bottom_top(points, p + 1, high)
  }
  return(points)
}

partition_bottom_top <- function(points, low, high) {
  pivot <- points[high, ]
  i <- low
  for (j in low:high) {
    if (points[j, ]$y < pivot$y) {
      points <- swap(points, i, j)
      i <- i + 1
    }
  }
  points <- swap(points, i, high)
  return(list('points' = points, 'p' = i))
}

Код quicksort_angle ()

quicksort_angle<-function(points, low, high, reference_point){
  if (low < high) {
    list_particion <- partition_angle(points, low, high, reference_point)
    p <- list_particion[['p']]
    points <- list_particion[['points']]
    points <- quicksort_angle(points, low, p - 1, reference_point)
    points <- quicksort_angle(points, p + 1, high, reference_point)
  }
  return(points)
}

partition_angle<-function(points, low, high, reference_point){
  pivot <- points[high,]
  i <- low
  for(j in low:high){
    if(less_angle(points[j,], pivot, reference_point)){
      points<-swap(points, i, j)
      i<-i+1
    }
  }
  points<-swap(points, i, high)
  return(list('points'=points, 'p'=i))
}

less_angle<-function(check_point, pivot_point, reference_point){
  # m_check = (check_point$y - reference_point$y) / (check_point$x - reference_point$x)
  # m_pivot = (pivot_point$y - reference_point$y) / (pivot_point$x - reference_point$x)


  m_check = (reference_point$y - check_point$y) / (reference_point$x - check_point$x)
  m_pivot = (reference_point$y - pivot_point$y) / (reference_point$x - pivot_point$x)

  if((m_pivot * m_check) < 0){
    return(m_check > m_pivot)
  }else{
    return(m_check < m_pivot)
  }
}

И пример использования (красный - это контрольная точка, а синий - отсортированные точки) enter image description here И вспомогательная функция swap ()

swap <- function(points, i, j) {
  temp <- points[i, ]
  points[i, ] <- points[j, ]
  points[j, ] <- temp
  return(points)
}

РЕДАКТИРОВАТЬ: пример данных (dput(head(points, n = 10)))

structure(list(x = c(407.377968561836, 320.925913405139, 806.236479437212, 
682.732644010102, 59.5188127646688, 195.601556062, 290.540224567521, 
449.757491987199, 760.845693320502, 544.514419493731), y = c(255.027047196869, 
63.905220010085, 922.746908532688, 956.717615367146, 244.059993790463, 
498.689062974649, 986.184075321769, 605.362085530069, 254.609465040965, 
711.789759260369)), row.names = c(NA, 10L), class = "data.frame")

РЕДАКТИРОВАТЬ: решения

Мое решение (vertices <- grahamScanConvexHull(points); dput(head(vertices, n = 10)))

structure(list(x = c(59.5188127646688, 320.925913405139), y = c(244.059993790463, 
63.905220010085)), row.names = c(10L, 1L), class = "data.frame")

Функция библиотеки (vertices <- points[chull(points),];dput(head(vertices, n = 10)))

structure(list(x = c(760.845693320502, 320.925913405139, 59.5188127646688, 
290.540224567521, 682.732644010102, 806.236479437212), y = c(254.609465040965, 
63.905220010085, 244.059993790463, 986.184075321769, 956.717615367146, 
922.746908532688)), row.names = c(9L, 2L, 5L, 7L, 4L, 3L), class = "data.frame")
...