【问题标题】:Getting two points on the edges of a rectangle获取矩形边缘上的两个点
【发布时间】:2019-07-29 19:44:09
【问题描述】:

我有一个矩形,想:

  1. 在一侧(任意)边上随机取一个点。
  2. 在一侧(除了之前选择的一侧)随机获取一个点。

我最初的方法是为每个可能的边创建数组。

var arr:Array = [[{x:0,y:0},           // Top
                  {x:width,y:0}],      //
                 [{x:width,y:0},       // Right
                  {x:width,y:height}], //
                 [{x:width,y:height},  // Bottom
                  {x:0,y:height}],     //
                 [{x:0,y:height},      // Left
                  {x:0,y:0}]];         //

然后,我得到双方。 randRand 的一个实例,具有以下方法: .next() 提供介于 01 之间的随机数 .between(x,y) 返回一个介于xy 之间的随机数。

var firstSide:Array = arr[rand.next() * arr.length];
var secondSide:Array;
do {
    secondSide = arr[rand.next() * arr.length];
} while(secondSide.equals(firstSide));

最后,我计算我的分数。

var pointOnFirstSide:Object = {x:rand.between(firstSide[0].x, firstSide[1].x),
                               y:rand.between(firstSide[0].y, firstSide[1].y};
var pointOnSecondSide:Object = {x:rand.between(secondSide[0].x, secondSide[1].x),
                                y:rand.between(secondSide[0].y, secondSide[1].y};

我认为这不是解决这个问题的最有效方法。

你会怎么做?

【问题讨论】:

  • 好吧,为清楚起见进行了编辑。我还没有运行它,但我正在尝试为这段代码了解一个一般概念。我会经常运行它并希望它高效。
  • 当它们相等时,我在 do while 循环中运行它们。当它们不相等时,循环中断并且代码继续。编辑:从来都不是!==,而是==
  • 随机性如何?如果tb 的边长是lr 的五倍,您是否希望选择其中之一的机会高五倍?或者您希望有同等机会选择四个边中的任何一个,然后有同等机会选择其余三个边之一?
  • 这是个好主意。我现在不是在处理一个正方形,但它足够接近,不会产生太大的影响。现在机会均等(为简单起见)。
  • 您的矩形是否始终与 x 和 y 轴对齐?如果不是,您选择的点几乎肯定不会位于矩形的边上。在任何情况下,当您只应该选择一个时,您每个点都选择了两个随机数。

标签: javascript arrays typescript math language-agnostic


【解决方案1】:

假设我们有以下接口和类型:

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; 

s1s2 代表我们选择的arr 的索引。第一个在0 和小于数组长度的一个之间选择一个整数。我们通过在0 和数组长度之间选择一个实数(好吧,浮点数,等等),然后取该实数的floor 来做到这一点。由于长度是4,我们所做的是在04 之间统一选择一个实数。其中四分之一在01 之间,另外四分之一在12 之间,另外四分之一在23 之间,最后一个季度在34 之间.这意味着您有 25% 的机会选择 0123。 (选择4 的机会基本上为0,或者如果randexcludes the upper bound 的正常方式实现,则可能正好为0。

对于s2,我们现在在1 和数组长度之间统一选择一个数字。在这种情况下,我们选择 123,每个都有 33% 的机会。我们将该数字添加到s1,然后在除以4 时取remainder。想想我们正在做的事情是从第一边s1 开始,然后顺时针移动 1、2 或 3 边(比如)以选择下边。这完全消除了选择同一边两次的可能性。


现在让我们看看在给定Rand的情况下,在线段(可以定义为PointPair,对应线段的两端p1p2)上随机选取一个点涉及什么实例:

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,表示从p1p2 我们想要走多远。如果frac0,我们选择p1。如果frac1,我们选择p2。如果frac0.5,我们在p1p2 之间选择。对此的一般公式是在给定frac 的情况下,在p1p2 之间进行线性插值。


希望在这两者之间,您可以实现您正在寻找的算法。祝你好运!

Link to code

【讨论】:

    【解决方案2】:

    jcalz 已经给出了很好的答案。这是我在 cmets 中询问的变体的替代版本:当您希望在周长的两侧均匀选择点时,如果您的 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 的计算。我们计算一个介于 0 和剩余三个边的总长度之间的随机数,方法是将所有四个边加在一起并减去当前边的长度,width 如果idx 是偶数,height 如果它是奇数。然后我们将它添加到包括索引在内的边的总长度(ceilfloor 调用只是计算水平和垂直边的数量,这些值分别乘以宽度和高度,然后加在一起),最后取结果与周长的浮点模数。这与 jcalz 的回答中的技术相同,但通过处理边长而不是简单的计数而变得更加复杂。

    我没有让rand 成为任何类或接口的实例,实际上这里也没有做任何 Typescript,但你可以很容易地自己添加。

    【讨论】:

      猜你喜欢
      • 2015-11-12
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多