生成随机路径的好算法是什么?

时间:2016-04-26 19:30:29

标签: random polygon random-walk

我需要生成一个包含25个段的随机路径,这些段永远不会在1000x1000区域中的两个位置之间穿过。做这个的好算法是什么?

我最初的想法是生成ok结果,是使用space partitioning method生成一个随机多边形,然后删除一边。

结果如下: output

这种方法的缺点是开始总是非常接近结束(因为它们最初是通过一条线连接的)。

另一个缺点是因为它们是多边形,整体形状会产生某种形状或扭曲的圆形。有许多类型的路径永远不会生成,如螺旋。

有人知道可以帮助我生成这些路径的算法吗?

2 个答案:

答案 0 :(得分:2)

这是一个想法(免责声明:脱离我的头脑,未经过测试,验证或任何其他......):

绘制随机坐标并“尝试”以您绘制的顺序连接线 - 所以你有 P1 (x1,y1) 然后 P2 (x2,y2) 并连接它们,然后 P3 (x3,y3) 并且只要没有创建交叉点(你有每次测试,你继续绘图和连接。最终,将生成一个交叉点 - 然后您尝试将最后一个点( Pn-1 :在新创建的点之前)连接到形成相交线的两个点中的较早点(让我们称之为这些 Pi & Pi + j 。如果这是有效的(意思是,它不会越过任何其他行),则断开该行( Pi + j Pi 之后不再出现,您将 Pi 连接到 Pn-1 并从 Pi + j 继续(现在根据点顺序变为 Pn-1 。如果将 Pn-1 连接到 Pi 无效,你可以做同样的事情,但是新发现的十字路口。

最终您将解决交叉点并将连接到最新点 - Pn ,您可以正常恢复。

该算法的明显缺点是它具有非常危险的Big-O时间复杂度,但它应该能够生成各种路径。

就实施数据结构而言,双重链表似乎是直接的候选人。

答案 1 :(得分:1)

对点集进行三角测量,然后在边缘上行走是一种解决方案,可避免基于多边形的行走所具有的“类似圆形”路径的问题。

您可能不想要一个点集太近的随机点集,因为它可能导致路径看起来就像它在某些点相交。出于这个原因,使用类似 poisson disk 采样方法来生成点集是可取的——这种点集的三角剖分将提供一条随机路径,其中段的长度或多或少相等,段-段角度大约 60°,没有明显的交点。

算法

手头有一个 Delaunay 三角剖分库,算法如下所示:

要初始化,

  • 生成点集
  • 三角点集
  • 从三角剖分中选择一个随机顶点(lastVisitedVertex

然后循环:

  • 选择一条连接到最后访问过的顶点的随机边,仅当连接到该边的另一个顶点还没有被访问过(这条边是下一段)
  • 将我们刚刚来自的顶点标记为已访问
  • lastVisitedVertex = 所选边的另一个顶点
  • 再次循环,如果路径长度超过我们想要的长度(即 > 25),则退出

插图

三角化泊松盘点集上的随机路径

[enter image description here

三角化随机点集上的随机路径 enter image description here

代码

以下是使用 Tinfour 库进行三角剖分的 Java 示例。使用库进行三角剖分时,我建议您选择一个可以轻松访问给定顶点的连接边以及构成给定边的顶点的库。 >

在这个例子中,我选择了一条随机边(而不是顶点)作为起点。

ArrayList<Vertex> randomPath(List<Vertex> randomPoints, int pathLength) {

    IncrementalTin tin = new IncrementalTin(10);

    tin.add(randomPoints, null); // insert point set; points are triangulated upon insertion

    HashSet<Vertex> visitedVertices = new HashSet<>();
    ArrayList<Vertex> pathVertices = new ArrayList<>();

    IQuadEdge lastEdge = tin.getStartingEdge(); // get random edge

    int n = 0;
    while (n <= pathLength) {

        List<IQuadEdge> list = new ArrayList<>();
        lastEdge.pinwheel().forEach(list::add); // get edges connected to A; add to array
        IQuadEdge nextEdge;
        int attempts = 0;
        do {
            nextEdge = list.get(random.nextInt(list.size())); // randomly select connected edge
            if (attempts++ > list.size() * 4) {
                return pathVertices; // path ended prematurely (no possible edges to take)
            }
        } while (visitedVertices.contains(nextEdge.getB()) || nextEdge.getB() == null);

        lastEdge = nextEdge.getDual(); // A and B flip around, so A is next vertex
        pathVertices.add(lastEdge.getB()); // add the point we just came from to path
        visitedVertices.add(lastEdge.getB()); // add the point we just came from to visited vertices
        n++;
    }
    return pathVertices;
}