2 回答
TA贡献1856条经验 获得超17个赞
jcalz 已经给出了很好的答案。这是我在评论中询问的变体的替代版本:当您希望在周长的两侧均匀选择您的点时,如果您的w : h比率为4 : 1,则第一个点位于水平边的可能性是 的四倍作为一个垂直的。(这意味着击中两个相反的长边的几率是 24/45;两个相反的短边的几率是 1/45;并且每个击中一个的几率是 20/45——通过一个简单但略显乏味的计算。)
const rand = {
next: () => Math. random (),
between: (lo, hi) => lo + (hi - lo) * Math .random (),
}
const vertices = (w, h) => [ {x: 0, y: h}, {x: w, y: h}, {x: w, y: 0}, {x: 0, y: 0} ]
const edges = ([v1, v2, v3, v4]) => [ [v1, v2], [v2, v3], [v3, v4], [v4, v1] ]
const randomPoint = ([v1, v2], rand) => ({
x: v1 .x + rand .next () * (v2 .x - v1 .x),
y: v1 .y + rand .next () * (v2 .y - v1 .y),
})
const getIndex = (w, h, x) => x < w ? 0 : x < w + h ? 1 : x < w + h + w ? 2 : 3
const twoPoints = (w, h, rand) => {
const es = edges (vertices (w, h) )
const perimeter = 2 * w + 2 * h
const r1 = rand .between (0, perimeter)
const idx1 = getIndex (w, h, r1)
const r2 = (
rand. between (0, perimeter - (idx1 % 2 == 0 ? w : h)) +
Math .ceil ((idx1 + 1) / 2) * w + Math .floor ((idx1 + 1) / 2) * h
) % perimeter
const idx2 = getIndex (w, h, r2)
return {p1: randomPoint (es [idx1], rand), p2: randomPoint (es [idx2], rand)}
}
console .log (
// Ten random pairs on rectangle with width 5 and height 2
Array (10) .fill () .map (() => twoPoints (5, 2, rand))
)
唯一复杂的位是 的计算r2。我们通过将所有四个边加在一起并减去当前边的长度(width如果idx是偶数,height如果是奇数)来计算 0 和其余三个边的总长度之间的随机数。然后我们把它加到边的总长度上,直到并包括索引(其中ceil和floor调用简单地计算水平和垂直边的数量,这些值分别乘以宽度和高度,并加在一起),最后取结果与周长的浮点模数。这与 jcalz 的答案中的技术相同,但通过处理边长而不是简单的计数而变得更加复杂。
我没有创建rand任何类或接口的实例,实际上这里也没有做任何 Typescript,但是你可以很容易地自己添加它。
TA贡献1863条经验 获得超2个赞
假设我们有以下接口和类型:
interface Rand {
next(): number;
between(x: number, y: number): number;
}
interface Point {
x: number;
y: number;
}
type PointPair = readonly [Point, Point];
并在评论中接受您的话,该程序是:首先随机选择两个边,然后在这些边上随机选择点......首先让我们看看随机选择两个边所涉及的内容:
const s1 = Math.floor(rand.between(0, arr.length));
const s2 = (Math.floor(rand.between(1, arr.length)) + s1) % arr.length;
s1并s2代表arr我们正在选择的索引。第一个选择介于0和 小于数组长度之间的整数。我们通过0在数组的 和之间选择一个实数(好吧,浮点数,无论如何),然后取该实数的下限来做到这一点。由于长度是4,我们正在做的是在0和之间均匀地选择一个实数4。这些数字有四分之一之间0和1之间,另外四分之一1和2之间,一个季度2和3最后一个季度,并且之间3和4。这意味着你有 25% 的机会选择每一个0,1,2和3。(选择的机会4基本上为 0,或者如果rand以排除上限的正常方式实现,则可能恰好为 0 )。
因为s2我们现在1在数组的长度和之间均匀地选择一个数字。在这种情况下,我们正在采摘1,2或3用33%的几率各。我们将这个数字加到s1然后除以的余数4。想想我们正在做的事情是从第一边开始s1,然后顺时针移动 1、2 或 3 边(比如)以选择下边。这完全消除了两次选择同一面的可能性。
现在让我们看看在给定一个实例的情况下PointPair,在一条线段(可以定义为 a ,对应于线段的两端p1和p2)上随机选取一个点涉及什么Rand:
function randomPointOnSide([p1, p2]: PointPair, rand: Rand): Point {
const frac = rand.next(); // between 0 and 1
return { x: (p2.x - p1.x) * frac + p1.x, y: (p2.y - p1.y) * frac + p1.y };
}
在这里,我们做的就是选择一个随机数frac,沿途有多远从代表p1到p2我们想去的地方。如果frac是0,我们选择p1。如果frac是1,我们选择p2。如果frac是0.5,我们选择介于p1和之间的中间位置p2。造成这种情况的通式是之间的线性插值p1和p2给出frac。
希望在这两者之间,您可以实现您正在寻找的算法。祝你好运!
添加回答
举报