我希望有一个函数,接受地理位置(纬度、经度),并围绕该位置生成随机坐标集合,但也将这些参数作为计算的一部分:
- 要生成的随机坐标数量
- 要生成的半径
- 随机坐标之间的最小距离(以米为单位)
- 要生成位置的根坐标。
以下是生成方式示例:
实现这个功能的好方法是什么?
我希望有一个函数,接受地理位置(纬度、经度),并围绕该位置生成随机坐标集合,但也将这些参数作为计算的一部分:
以下是生成方式示例:
实现这个功能的好方法是什么?
在一个位置周围生成随机坐标
function generateRandomPoint($centre, $radius) {
$radius_earth = 3959; //miles
//Pick random distance within $distance;
$distance = lcg_value()*$radius;
//Convert degrees to radians.
$centre_rads = array_map( 'deg2rad', $centre );
//First suppose our point is the north pole.
//Find a random point $distance miles away
$lat_rads = (pi()/2) - $distance/$radius_earth;
$lng_rads = lcg_value()*2*pi();
//($lat_rads,$lng_rads) is a point on the circle which is
//$distance miles from the north pole. Convert to Cartesian
$x1 = cos( $lat_rads ) * sin( $lng_rads );
$y1 = cos( $lat_rads ) * cos( $lng_rads );
$z1 = sin( $lat_rads );
//Rotate that sphere so that the north pole is now at $centre.
//Rotate in x axis by $rot = (pi()/2) - $centre_rads[0];
$rot = (pi()/2) - $centre_rads[0];
$x2 = $x1;
$y2 = $y1 * cos( $rot ) + $z1 * sin( $rot );
$z2 = -$y1 * sin( $rot ) + $z1 * cos( $rot );
//Rotate in z axis by $rot = $centre_rads[1]
$rot = $centre_rads[1];
$x3 = $x2 * cos( $rot ) + $y2 * sin( $rot );
$y3 = -$x2 * sin( $rot ) + $y2 * cos( $rot );
$z3 = $z2;
//Finally convert this point to polar co-ords
$lng_rads = atan2( $x3, $y3 );
$lat_rads = asin( $z3 );
return array_map( 'rad2deg', array( $lat_rads, $lng_rads ) );
}
使用方法
generateRandomPoint(array(3.1528, 101.7038), 4);
for each point to generate "n"
find a random angle
get the x and y from the angle * a random radius up to max radius
for each point already generated "p"
calculate the distance between "n" and "p"
if "n" satisfies the min distance
add new point "n"
$angle = deg2rad(mt_rand(0, 359));
$pointRadius = mt_rand(0, $radius);
$point = array(
'x' => sin($angle) * $pointRadius,
'y' => cos($angle) * $pointRadius
);
$distance = sqrt(pow($n['x'] - $p['x'], 2) + pow($n['y'] - $p['y'], 2));
** 编辑 **
为了澄清其他人所说的话,并进行了进一步的研究(我不是数学家,但评论确实让我想),这里提供一个最简单的高斯分布定义:
如果您在一维空间中,则 $pointRadius = $x * mt_rand(0, $radius); 可以正常使用,因为当 $x 具有高斯分布时,$radius 和 $x之间没有区别。
然而,在二维或更高维空间中,如果坐标($x,$y,...)具有高斯分布,则半径 $radius 不具有高斯分布。
实际上,在2维[或k维]中,$radius^2 的分布被称为“具有2 [或k]自由度的卡方分布”,前提是($x,$y,...)是独立的,并且具有零均值和相等方差。
因此,要有正态分布,您需要将生成的半径行更改为
$pointRadius = sqrt(mt_rand(0, $radius*$radius));
O(n^2)
,而它很容易达到O(n log n)
。 - Daniel0
和359000
之间取一个随机值,并将结果除以1000
来获得千分精度的值,等等,从而轻松改善精度。是的,半径也应该是随机的 :) - Yanick Rochon$angle = deg2rad(mt_rand(0, 359));
$radius = sqrt(mt_rand(0, $max_radius * $max_radius));
其次,按照之前的描述检查与先前点的距离。
最后,不要忘记可以达到无法生成更多点的状态,因此您可能需要在“尝试和丢弃”循环中设置上限,以避免在空间(接近)充满时进入无限循环。
附注:正如另一篇答案上所说,这是O(n^2),因此不适用于大量的点。您可以通过按半径对点进行排序并仅考虑那些半径差小于 $min_distance
的点来解决这个问题,只要 $min_distance << $max_radius
(如您的绘图所示);做得比这更好需要使用更复杂的解决方案(例如,在较大半径处还使用角度,或者使用单独的四叉树存储和比较位置)。但是对于数十个点,我想这是不必要的。
mt_rand()
函数返回的是一个整数,这不是我们需要的。我们可以使用以下方法获取随机角度:$angle = lcg_value() * 2 * pi()
。并且使用以下方法获取随机半径:$radius = sqrt(lcg_value() * $max_radius * $max_radius)
。 - Karolis