将x点均匀分布在一个圆内

Uniformly distribute x points inside a circle

我想在一个圆圈内均匀分布一组预定的点。通过均匀分布,我的意思是它们彼此之间的距离应该相等(因此随机方法不起作用)。我尝试了六边形方法,但一直无法到达最外半径。

我目前的方法是嵌套 for 循环,其中每次外部迭代都会减少半径和点数,并且每个内部循环会在新半径上均匀地丢弃点。本质上,它是一堆嵌套的圆圈。不幸的是,这远非平衡。有关如何正确执行此操作的任何提示?

区域内均匀分布和边界上均匀分布的目标冲突;任何解决方案都是两者之间的妥协。我用一个附加参数 alpha 扩充了 sunflower seed arrangement,它表示人们对边界均匀度的关心程度。

alpha=0给出了典型的向日葵排列,边界呈锯齿状:

使用alpha=2边界更平滑:

(进一步增加 alpha 是有问题的:边界上的点太多)。

算法放置n个点,其中第k个点放置在距离边界sqrt(k-1/2)处(索引以k=1开头),并且极角 2*pi*k/phi^2 其中 phi 是黄金比例。例外:最后 alpha*sqrt(n) 个点放在圆的外边界上,其他点的极半径被缩放以说明这一点。极半径的计算是在函数 radius.

中完成的

编码在MATLAB.

function sunflower(n, alpha)   %  example: n=500, alpha=2
    clf
    hold on
    b = round(alpha*sqrt(n));      % number of boundary points
    phi = (sqrt(5)+1)/2;           % golden ratio
    for k=1:n
        r = radius(k,n,b);
        theta = 2*pi*k/phi^2;
        plot(r*cos(theta), r*sin(theta), 'r*');
    end
end

function r = radius(k,n,b)
    if k>n-b
        r = 1;            % put on the boundary
    else
        r = sqrt(k-1/2)/sqrt(n-(b+1)/2);     % apply square root
    end
end

偶然发现了这个问题和 (所以所有的信任都归功于 user3717023 和 Matt)。
只需在此处将我的翻译添加到 R 中,以防其他人需要:)

library(tibble)
library(dplyr)
library(ggplot2)

sunflower <- function(n, alpha = 2, geometry = c('planar','geodesic')) {
  b <- round(alpha*sqrt(n))  # number of boundary points
  phi <- (sqrt(5)+1)/2  # golden ratio

  r <- radius(1:n,n,b)
  theta <- 1:n * ifelse(geometry[1] == 'geodesic', 360*phi, 2*pi/phi^2)

  tibble(
    x = r*cos(theta),
    y = r*sin(theta)
  )
}

radius <- function(k,n,b) {
  ifelse(
    k > n-b,
    1,
    sqrt(k-1/2)/sqrt(n-(b+1)/2)
  )
}

# example:
sunflower(500, 2, 'planar') %>%
    ggplot(aes(x,y)) +
    geom_point()

不妨在我的 Python 翻译上加标签。

from math import sqrt, sin, cos, pi
phi = (1 + sqrt(5)) / 2  # golden ratio

def sunflower(n, alpha=0, geodesic=False):
    points = []
    angle_stride = 360 * phi if geodesic else 2 * pi / phi ** 2
    b = round(alpha * sqrt(n))  # number of boundary points
    for k in range(1, n + 1):
        r = radius(k, n, b)
        theta = k * angle_stride
        points.append((r * cos(theta), r * sin(theta)))
    return points

def radius(k, n, b):
    if k > n - b:
        return 1.0
    else:
        return sqrt(k - 0.5) / sqrt(n - (b + 1) / 2)


# example
if __name__ == '__main__':
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    points = sunflower(500, alpha=2, geodesic=False)
    xs = [point[0] for point in points]
    ys = [point[1] for point in points]
    ax.scatter(xs, ys)
    ax.set_aspect('equal') # display as square plot with equal axes
    plt.show()

建立在 @OlivelsAWord 之上,这是一个使用 numpy 的 Python 实现:

import numpy as np
import matplotlib.pyplot as plt


def sunflower(n: int, alpha: float) -> np.ndarray:
    # Number of points respectively on the boundary and inside the cirlce.
    n_exterior = np.round(alpha * np.sqrt(n)).astype(int)
    n_interior = n - n_exterior

    # Ensure there are still some points in the inside...
    if n_interior < 1:
        raise RuntimeError(f"Parameter 'alpha' is too large ({alpha}), all "
                           f"points would end-up on the boundary.")
    # Generate the angles. The factor k_theta corresponds to 2*pi/phi^2.
    k_theta = np.pi * (3 - np.sqrt(5))
    angles = np.linspace(k_theta, k_theta * n, n)

    # Generate the radii.
    r_interior = np.sqrt(np.linspace(0, 1, n_interior))
    r_exterior = np.ones((n_exterior,))
    r = np.concatenate((r_interior, r_exterior))

    # Return Cartesian coordinates from polar ones.
    return r * np.stack((np.cos(angles), np.sin(angles)))

    # NOTE: say the returned array is called s. The layout is such that s[0,:]
    # contains X values and s[1,:] contains Y values. Change the above to
    #   return r.reshape(n, 1) * np.stack((np.cos(angles), np.sin(angles)), axis=1)
    # if you want s[:,0] and s[:,1] to contain X and Y values instead.


if __name__ == '__main__':
    fig, ax = plt.subplots()

    # Let's plot three sunflowers with different values of alpha!
    for alpha in (0, 1, 2):
        s = sunflower(500, alpha)
        # NOTE: the 'alpha=0.5' parameter is to control transparency, it does
        # not have anything to do with the alpha used in 'sunflower' ;)
        ax.scatter(s[0], s[1], alpha=0.5, label=f"alpha={alpha}")
    
    # Display as square plot with equal axes and add a legend. Then show the result :)
    ax.set_aspect('equal')
    ax.legend()
    plt.show()