菱形方算法

Gab*_*lla 21 java algorithm

我正在尝试用Java 编写Diamond-Square算法来生成随机地图,但无法弄清楚实现...

任何人都有一些Java代码(或其他语言)所以我可以检查如何制作循环将非常感谢!

谢谢!

M. *_*sup 26

这是一种用于生成值的有趣算法.这是我根据维基百科文章的参考资料此页面上给出的解释创建的实现.它将创建"球形值"(包裹在所有边缘).评论中有注释如何更改它以在边缘上生成新值而不是包装(尽管在这些情况下边缘的平均值不正确).

//size of grid to generate, note this must be a
//value 2^n+1
final int DATA_SIZE = 9;
//an initial seed value for the corners of the data
final double SEED = 1000.0;
double[][] data = new double[DATA_SIZE][DATA_SIZE];
//seed the data
data[0][0] = data[0][DATA_SIZE-1] = data[DATA_SIZE-1][0] = 
  data[DATA_SIZE-1][DATA_SIZE-1] = SEED;

double h = 500.0;//the range (-h -> +h) for the average offset
Random r = new Random();//for the new value in range of h
//side length is distance of a single square side
//or distance of diagonal in diamond
for(int sideLength = DATA_SIZE-1;
    //side length must be >= 2 so we always have
    //a new value (if its 1 we overwrite existing values
    //on the last iteration)
    sideLength >= 2;
    //each iteration we are looking at smaller squares
    //diamonds, and we decrease the variation of the offset
    sideLength /=2, h/= 2.0){
  //half the length of the side of a square
  //or distance from diamond center to one corner
  //(just to make calcs below a little clearer)
  int halfSide = sideLength/2;

  //generate the new square values
  for(int x=0;x<DATA_SIZE-1;x+=sideLength){
    for(int y=0;y<DATA_SIZE-1;y+=sideLength){
      //x, y is upper left corner of square
      //calculate average of existing corners
      double avg = data[x][y] + //top left
      data[x+sideLength][y] +//top right
      data[x][y+sideLength] + //lower left
      data[x+sideLength][y+sideLength];//lower right
      avg /= 4.0;

      //center is average plus random offset
      data[x+halfSide][y+halfSide] = 
    //We calculate random value in range of 2h
    //and then subtract h so the end value is
    //in the range (-h, +h)
    avg + (r.nextDouble()*2*h) - h;
    }
  }

  //generate the diamond values
  //since the diamonds are staggered we only move x
  //by half side
  //NOTE: if the data shouldn't wrap then x < DATA_SIZE
  //to generate the far edge values
  for(int x=0;x<DATA_SIZE-1;x+=halfSide){
    //and y is x offset by half a side, but moved by
    //the full side length
    //NOTE: if the data shouldn't wrap then y < DATA_SIZE
    //to generate the far edge values
    for(int y=(x+halfSide)%sideLength;y<DATA_SIZE-1;y+=sideLength){
      //x, y is center of diamond
      //note we must use mod  and add DATA_SIZE for subtraction 
      //so that we can wrap around the array to find the corners
      double avg = 
        data[(x-halfSide+DATA_SIZE)%DATA_SIZE][y] + //left of center
        data[(x+halfSide)%DATA_SIZE][y] + //right of center
        data[x][(y+halfSide)%DATA_SIZE] + //below center
        data[x][(y-halfSide+DATA_SIZE)%DATA_SIZE]; //above center
      avg /= 4.0;

      //new value = average plus random offset
      //We calculate random value in range of 2h
      //and then subtract h so the end value is
      //in the range (-h, +h)
      avg = avg + (r.nextDouble()*2*h) - h;
      //update value for center of diamond
      data[x][y] = avg;

      //wrap values on the edges, remove
      //this and adjust loop condition above
      //for non-wrapping values.
      if(x == 0)  data[DATA_SIZE-1][y] = avg;
      if(y == 0)  data[x][DATA_SIZE-1] = avg;
    }
  }
}

//print out the data
for(double[] row : data){
  for(double d : row){
    System.out.printf("%8.3f ", d);
  }
  System.out.println();
}
Run Code Online (Sandbox Code Playgroud)

  • “所有边缘都包裹”不是一个球体。这是一个环形线圈。你做了一个甜甜圈,而不是一个地球仪。向北走得越远,地球仪的周长就越小。甜甜圈则不然。-- 不是贬低几何形状,它*方式*更容易,但从任何角度来看它都不是一个球体。 (2认同)

Chr*_*ley 14

M. Jessup的答案似乎有些小问题.他在哪里:

      double avg = 
        data[(x-halfSide+DATA_SIZE)%DATA_SIZE][y] + //left of center
        data[(x+halfSide)%DATA_SIZE][y] + //right of center
        data[x][(y+halfSide)%DATA_SIZE] + //below center
        data[x][(y-halfSide+DATA_SIZE)%DATA_SIZE]; //above center

它应该改为:

      double avg = 
        data[(x-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)][y] + //left of center
        data[(x+halfSide)%(DATA_SIZE-1)][y] + //right of center
        data[x][(y+halfSide)%(DATA_SIZE-1)] + //below center
        data[x][(y-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)]; //above center

否则它从错误的位置读取(可能是未初始化的).


Ber*_*sms 5

对于任何人来说,这里是由M. Jessup提供的算法,包含在一个接收种子的类中(允许重现结果),n的值用于指定维度(维度为2 ^ n + 1),并暴露结果作为规范化的浮点数组.它还修复了应用算法的第二部分.

import java.util.Random;

public class DiamondSquare {

public float[][] data;
public int width;
public int height;

public DiamondSquare(long mseed, int n) {
    //size of grid to generate, note this must be a
    //value 2^n+1
    int DATA_SIZE = (1 << n) + 1;
    width = DATA_SIZE;
    height = DATA_SIZE;
    //an initial seed value for the corners of the data
    final float SEED = 1000.0f;
    data = new float[DATA_SIZE][DATA_SIZE];
    //seed the data
    data[0][0] = data[0][DATA_SIZE-1] = data[DATA_SIZE-1][0] = 
            data[DATA_SIZE-1][DATA_SIZE-1] = SEED;

    float valmin = Float.MAX_VALUE;
    float valmax = Float.MIN_VALUE;

    float h = 500.0f;//the range (-h -> +h) for the average offset
    Random r = new Random(mseed);//for the new value in range of h
    //side length is distance of a single square side
    //or distance of diagonal in diamond
    for(int sideLength = DATA_SIZE-1;
            //side length must be >= 2 so we always have
            //a new value (if its 1 we overwrite existing values
            //on the last iteration)
            sideLength >= 2;
            //each iteration we are looking at smaller squares
            //diamonds, and we decrease the variation of the offset
            sideLength /=2, h/= 2.0){
        //half the length of the side of a square
        //or distance from diamond center to one corner
        //(just to make calcs below a little clearer)
        int halfSide = sideLength/2;

        //generate the new square values
        for(int x=0;x<DATA_SIZE-1;x+=sideLength){
            for(int y=0;y<DATA_SIZE-1;y+=sideLength){
                //x, y is upper left corner of square
                //calculate average of existing corners
                float avg = data[x][y] + //top left
                        data[x+sideLength][y] +//top right
                        data[x][y+sideLength] + //lower left
                        data[x+sideLength][y+sideLength];//lower right
                avg /= 4.0;

                //center is average plus random offset
                data[x+halfSide][y+halfSide] = 
                        //We calculate random value in range of 2h
                        //and then subtract h so the end value is
                        //in the range (-h, +h)
                        avg + (r.nextFloat()*2*h) - h;

                valmax = Math.max(valmax, data[x+halfSide][y+halfSide]);
                valmin = Math.min(valmin, data[x+halfSide][y+halfSide]);
            }
        }

        //generate the diamond values
        //since the diamonds are staggered we only move x
        //by half side
        //NOTE: if the data shouldn't wrap then x < DATA_SIZE
        //to generate the far edge values
        for(int x=0;x<DATA_SIZE-1;x+=halfSide){
            //and y is x offset by half a side, but moved by
            //the full side length
            //NOTE: if the data shouldn't wrap then y < DATA_SIZE
            //to generate the far edge values
            for(int y=(x+halfSide)%sideLength;y<DATA_SIZE-1;y+=sideLength){
                //x, y is center of diamond
                //note we must use mod  and add DATA_SIZE for subtraction 
                //so that we can wrap around the array to find the corners
                float avg = 
                        data[(x-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)][y] + //left of center
                        data[(x+halfSide)%(DATA_SIZE-1)][y] + //right of center
                        data[x][(y+halfSide)%(DATA_SIZE-1)] + //below center
                        data[x][(y-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)]; //above center
                avg /= 4.0;

                //new value = average plus random offset
                //We calculate random value in range of 2h
                //and then subtract h so the end value is
                //in the range (-h, +h)
                avg = avg + (r.nextFloat()*2*h) - h;
                //update value for center of diamond
                data[x][y] = avg;

                valmax = Math.max(valmax, avg);
                valmin = Math.min(valmin, avg);


                //wrap values on the edges, remove
                //this and adjust loop condition above
                //for non-wrapping values.
                if(x == 0)  data[DATA_SIZE-1][y] = avg;
                if(y == 0)  data[x][DATA_SIZE-1] = avg;
            }
        }
    }


    for(int i=0; i<width; i++) {
        for(int j=0; j<height; j++) { 
            data[i][j] = (data[i][j] - valmin) / (valmax - valmin); 
        } 
    }

}
}
Run Code Online (Sandbox Code Playgroud)