getRandomCircle =
Compile[{{xMin, _Real}, {xMax, _Real}, {yMin, _Real}, {yMax, \
_Real}, {n, _Integer}, {d, _Real}, {u, _Real}, {sigma, _Real}},
Module[{data, num = 0, i, x = 0., y = 0., r = 0., flag = True},
data = Table[{0., 0., 0.}, {n}];
(*第一个圆*)
data[[1, 3]] = Abs@RandomVariate[NormalDistribution[u, sigma]];
data[[1, 1]] =
RandomReal[{xMin + data[[1, 3]], xMax - data[[1, 3]]}];
data[[1, 2]] =
RandomReal[{yMin + data[[1, 3]], yMax - data[[1, 3]]}];
num++;
(*后面的圆*)
While[num <= n - 1,
While[True,
x = RandomReal[{xMin, xMax}]; y = RandomReal[{yMin, yMax}];
r = Abs@RandomVariate[NormalDistribution[u, sigma]];
If[xMin <= x - r && xMax >= x + r && yMin <= y - r &&
yMax >= y + r, Break[]];
];
flag = True;
For[i = 1, i <= num, i++,
If[Sqrt[(x - data[[i, 1]])^2 + (y - data[[i, 2]])^2] <
data[[i, 3]] + r + d, flag = False; Break[]]
];
If[flag, num++; data[[num, 1]] = x; data[[num, 2]] = y;
data[[num, 3]] = r];
];
data
], CompilationTarget -> "C", RuntimeOptions -> "Speed"
];
输入参数:前四个是矩形的范围,生成n个圆,每两个圆之间的距离需要大于等于d,圆的半径生成按照Abs[满足参数u和sigma的正态分布的随机数]。
问题1:需要输入合适的参数,不合适的参数,例如圆的半径平均值很大,那么越到后来越难产生满足条件的圆。以下面例子给的参数为例,{0, 10, 0, 10, 1000, 0.1, 0.5, 0.3} 看上去产生1000个圆只要产生1000个半径就可以了,实际上产生了212330个半径才找到1000个能满足要求放在一起的圆。如果此时改成d=0.15,那么程序运行时间从0.2s增加到8s,d=0.16的运行时间是81s。当然也可以缩小u和sigma来缩短时间。
问题2:实际输出的圆的半径并不满足正态分布,因为到后期按照正态分布产生的较大数值的半径(例如半径为1)是几乎不可能距离已经产生的圆都大于d的,此时这个半径被抛弃。所以可以看到最终的结果半径数值小的比例被人为提高了。可以联想一下,你往杯子里扔大小随机变化的石头,扔到后来其实就不是大小随机了,因为大的石头塞不进去了,只能塞小的石头。
使用举例:
Graphics[Circle[{#1, #2}, #3] & @@@
getRandomCircle[0, 10, 0, 10, 1000, 0.1, 0.5, 0.3]]