简体   繁体   English

给定圆上的点,找到可通过中心线分开的最小点数

[英]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. 我已经基于@db建议编写了以下内容,但是该算法对于tst4失败。

In R, 在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. 对于我在代码中提供的度数为15、45和225的三个点,我可以用一条线分隔的最小点数(例如m)将为1。

For the points with degrees 15, 20, 25, the answer would obviously be 0. 对于度数为15、20、25的点,答案显然为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. 这是如果您要通过R代码运行的曲线图,并以一行示例说明可以分离的最小点数(即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. 我还更新了上面的代码,该代码允许绘制数据点,推测最小化m的直径,并按度数绘制直径。

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 . 如果角度差<180 ,则增加右索引;如果角度差>180 ,则增加左索引。 Minimum from (right-left, length-right+left) is your desired value. 最小值(right-left, length-right+left)为您想要的值)。

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 ) 请注意,扫描应该循环的方式执行(你可以用+360除了像这样添加列表中的副本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. 只需在所有角度( 0.5:359.5 )上画一条线,然后看看哪个角度值最小即可。

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 

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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