【问题标题】:Terrain/Mountain algorithm not working as intended地形/山地算法未按预期工作
【发布时间】:2023-03-16 16:24:01
【问题描述】:

我想使用一个非常基本的原理创建一个上面有山的地形,如下图所示:

0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 2 1 0 0 0 0
0 0 0 1 2 3 2 1 0 0 0
0 0 1 2 3 4 3 2 1 0 0
0 0 0 1 2 3 2 1 0 0 0
0 0 0 0 1 2 1 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0

它从height = 4 的随机点开始,然后逐渐减少它的邻居。

递归的思路很简单,我开始一个点,用height - 1(在这个例子中)递归到上/下/左/右,只有还没有遇到,我才设置它们的值。

我是这样实现的:

private void createMountain(final float[][] heightMapping, final float startHeight) {
    boolean[][] traversed = new boolean[width][depth];
    boolean positive = (startHeight >= 0f);
    int x = random.nextInt(width);
    int z = random.nextInt(depth);
    recursiveUpdate(heightMapping, traversed, x, z, startHeight, positive);
}

private void recursiveUpdate(final float[][] heightMapping, final boolean[][] traversed, final int x, final int z, final float startHeight, final boolean positive) {
    if (x < 0 || x >= width || z < 0 || z >= depth) {
        return;
    }
    if (traversed[x][z]) {
        return;
    }
    if ((positive && startHeight <= 0f) || (!positive && startHeight >= 0f)) {
        heightMapping[x][z] = 0f;
        return;
    }
    traversed[x][z] = true;
    heightMapping[x][z] = startHeight;
    recursiveUpdate(heightMapping, traversed, x, z - 1, calculateNewStartHeight(startHeight, positive), positive);
    recursiveUpdate(heightMapping, traversed, x, z + 1, calculateNewStartHeight(startHeight, positive), positive);
    recursiveUpdate(heightMapping, traversed, x - 1, z, calculateNewStartHeight(startHeight, positive), positive);
    recursiveUpdate(heightMapping, traversed, x + 1, z, calculateNewStartHeight(startHeight, positive), positive);
}

private float calculateNewStartHeight(final float startHeight, final boolean positive) {
    float delta = startHeight / maxDecayFactor;
    return (positive) ? startHeight - delta : startHeight + delta;
}

但是它给了我以下输出:

0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  
0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.1  
1.9  1.6  1.2  1.0  0.8  0.6  0.5  0.4  0.3  0.3  0.2  0.2  0.1  0.1  0.1  0.1  
2.4  3.0  3.8  4.7  5.9  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.1 

问题是它现在形成一条线,这不是我的意图,而不是逐渐平滑。

如何实现我想要的算法?

【问题讨论】:

    标签: java algorithm recursion terrain


    【解决方案1】:

    您的递归方法的问题在于它基本上执行DFS,因此您总是沿着最长的分支朝一个方向前进。但是这个分支总是在腐烂。
    由于您还维护一个 traversed 集 - 它错误地确保您稍后不会访问另一个分支(另一个递归调用)的相同顶点。

    有两种方法可以解决这个问题:

    1. 更优雅,可能更高效 - 将算法从 DFS 更改为 BFS。更改距离源 1 处的单元格,然后更改距离 2 处的单元格,依此类推...
    2. 不太优雅 - 但需要对代码进行最少的更改:更改算法的停止条件,而不是 if (traversed[x][z]) { return; } 执行类似 if (heightMapping[x][z] &gt; startHeight) { return; } 的操作。这将确保您可以更新高度(如果它应该更高并按预期工作)。

    BFS 更新应该类似于(伪代码):

    Q <- new Queue() //or even better - priority queue that holds the highest point at the top
    Q.push((x,y,height)
    visited[width][depth]; //init as all false
    while Q.empty() == false:
       curr <- Q.pop()
       if (sanity check for x<0 , y< 0 ,..):
          continue
       if visited[x][y] == true:
          continue
       if height <= 0: //or some epsilon instead of 0 if there are floating point issues
          continue
       heights[x][y] = height
       visited[x][y] = true
       Q.push(x+1,y,calculateNewHeight(...))
       ... //similarly for all directions
    

    【讨论】:

    • 啊,这确实解释了,真有趣,我下周开始在大学学习算法课程......我没有意识到我正在实施这样的算法。
    【解决方案2】:

    不需要递归。

    假设您希望山顶在 x, y 位置的高度为 h。

    height(x,y) = h
    for dist = 1 to h-1
      for x' = x - dist to x + dist
         x_dist = abs(x - x')
         y_dist = dist - x_dist
         height(x', y + y_dist) = h - dist
         height(x', y - y_dist) = h - dist
    

    您在距峰顶的任何给定距离处的高度都是山峰的高度减去与峰顶的正交距离。我假设您从全零开始,因此如果您离峰值足够远,则不需要设置这些值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      相关资源
      最近更新 更多