简体   繁体   中英

Given points on a circle, find the minimum number of points which can be separated with a line through the center

Assume you are given a vector of degrees which represent points on the unit circle. How could you formally check to see what the minimum number of points you could isolate in one semicircle with a diameter? I understand there may be multiple diameters which satisfy this property for a given set of data points. That is okay. I am solely interested in the minimum number of points which can be isolated, not the diameter in particular. It also needs to be computationally efficient, so it works for a large number of points. I have written the following based on @db suggestion, but the algorithm fails for tst4.

In R,

# Plots the points on a circle and attempts to find the minimum m (algorithm incorrect for tst )
min_dia <- function(degs, plot = T){
  library(dplyr)

  plot_circle <- function(x, y, r) {
    angles <- seq(0, 2*pi,length.out = 360)
    lines(r*cos(angles) + x, r*sin(angles) + y)
  }

  deg <- degs
  plot_boo <- plot

 # @d.b suggestion method for finding m
  temp <- abs((deg - min(deg) + 180) %% 360 - 180)
  m <- min(table(cut(temp, breaks = c(-180, 90, 180))))

  if(plot_boo == T){
    tm_deg <- c(0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330)
    tm_rad <- (tm_deg * pi) / 180
    th <- (deg*pi)/180
    r <- 1
    x <- r*cos(th)
    y <- r*sin(th)
    windows.options(width = 600, height = 600)
    plot(x, y, xlim = c(-1.1, 1.1), ylim = c(-1.1, 1.1), pch = 20, xlab = "", ylab = "", main = "Plot of Given Data Points by Degrees")
    plot_circle(0, 0, 1)
    points(0, 0)
    text(r*cos(tm_rad), r*sin(tm_rad), labels = paste0(tm_deg), cex= 0.5, pos = 3)
  }

  return(m)
}

# Function to plot diameter by degrees
plot_dia <- function(deg){
  deg1 <- deg
  deg2 <- deg + 180
  th1 <- (deg1*pi)/180
  th2 <- (deg2*pi)/180
  x1 <- cos(th1)
  y1 <- sin(th1)
  x2 <- cos(th2)
  y2 <- sin(th2)
  lines(c(x1, x2), c(y1, y2))
}

# Testing
tst1 <- c(15, 45, 20) # m = 0
tst2 <- c(15, 45, 200) # m = 1
tst3 <- c(15, 46, 114, 137, 165, 187, 195, 215, 271, 328) # m = 3
tst4 <- c(36, 304, 281, 254, 177, 59, 47, 158, 244, 149, 317, 235, 345, 209, 204,
          156, 325, 95, 215, 267)

# Implementation
min_dia(tst1)
plot_dia(90) # eyeball and plot to check

min_dia(tst2)
plot_dia(190) # eyeball and plot to check

min_dia(tst3)
plot_dia(110) # eyeball and plot to check

min_dia(tst4)
plot_dia(150) # m is probably 2

For the three points I have provided in the code with degrees 15, 45, and 225, the minimum number of points (say m) I could separate with a line would be 1.

For the points with degrees 15, 20, 25, the answer would obviously be 0.

Any help or guidance on an efficient algorithm to solve this minimization problem would be greatly appreciated.

Update:

Here is the plot if you were to run through the R code along with an example of a line which illustrates the minimum number of points you could separate, being 1.

15、45和225度的示例

Update:

I have also updated the code above which allows one to plot the data points, speculate a diameter which minimizes m, and the plot the diameter by degree.

If points are not sorted, then sort them by angle.

Walk through the list using two-pointer approach. Increment right index if angle difference is <180 , increment left index if angle difference is >180 . Minimum from (right-left, length-right+left) is your desired value.

Note that scanning should be performed in cyclic manner (you may add copy of the list with +360 addition like this 15, 45, 225, 375, 585 )

Here is a brute force method. Just draw a line at all angle ( 0.5:359.5 ) and see what angle gives the least value.

bar = function(degs){
    CUTS = sapply(0:359 + 0.5, function(D){
        temp = ((degs - D + 180) %% 360 - 180)
        min(table(cut(temp, breaks = c(-180, 0, 180))))
    })

    D = (0:359 + 0.5)[which.min(CUTS)]
    m = min(CUTS)

    plot(0, 0, type = "n",
    xlim = c(-1.5, 1.5), ylim = c(-1.5, 1.5),
    ann = FALSE, axes = FALSE, asp = 1)
    plotrix::draw.circle(0, 0, 1)

    degs = degs * pi/180
    xs = cos(degs)
    ys = sin(degs)

    x1 = cos(D * pi/180)
    y1 = sin(D * pi/180)

    x2 = cos((D * pi/180) + pi)
    y2 = sin((D * pi/180) + pi)

    lines(c(x1, x2), c(y1, y2))
    points(xs, ys, pch = 19)
    points(0, 0)
    return(c(min_num = m, angle = D))
}

tst4 <- c(36, 304, 281, 254, 177, 59, 47, 158, 244, 149, 317, 235,
             345, 209, 204, 156, 325, 95, 215, 267)

bar(degs = tst4)
# min_num   angle 
#     5.0   145.5 

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM