【问题标题】:Diamond square algorithm菱形平方算法
【发布时间】:2010-05-03 01:36:38
【问题描述】:

我正在尝试用 Java 编写 Diamond-Square algorithm 以生成随机映射,但无法弄清楚实现...

任何拥有一些 Java 代码(或其他语言)的人都将不胜感激!

谢谢!

【问题讨论】:

    标签: java algorithm


    【解决方案1】:

    这是一个用于生成值的有趣算法。这是我根据this page in the references from the wikipedia article 的解释创建的一个实现。它将创建“球形值”(包裹在所有边缘)。 cmets 中有关于如何更改它以在边缘而不是环绕上生成新值的注释(尽管在这些情况下边缘的平均值的含义并不真正正确)。

    //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();
    }
    

    【讨论】:

    • 当您使用大量迭代时,您是否会出现峰值和低谷?
    • “包裹在所有边缘”不是球体。这是一个环形线圈。你做了一个甜甜圈,而不是一个地球仪。越往北走,地球仪的圆周就越小。甜甜圈没有。 -- 不破坏几何,它WAY更容易,但它不是任何度量标准的球体。
    • 有没有办法让算法考虑百分比?我已经对其进行了测试,似乎有时我可能会在一侧或另一侧得到非常有偏见的价值观。我基本上为我的每种类型设置了一定的范围,我希望它们以一定的频率出现(例如 type1 应该以 10% 的机会出现,type2 应该以 30% 的机会出现,等等......)。有办法吗?由于值从 -h 到 +h 分布不均匀,我看不到一个简单的出路。
    • 那么h * 2 是最高点和最低点之间的最大可能差异吗?我正在尝试这样做,但我想通过分配高度来做到这一点,所以我可以使用height / 2 并将其用于相同的目的吗?这就是h 的意思吗?顺便说一句,很好的解决方案!
    【解决方案2】:

    M. Jessup 的回答似乎有点错误。他在哪里:

                                                               =
    data[(x-halfSide+DATA_SIZE)%DATA_SIZE][y] + //中心左侧
    data[(x+halfSide)%DATA_SIZE][y] + //中心右侧
    data[x][(y+halfSide)%DATA_SIZE] + //中心下方
    数据[x][(y-halfSide+DATA_SIZE)%DATA_SIZE]; //高于中心
    

    应该改为:

                                                               =
    data[(x-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)][y] + //中心左侧
    data[(x+halfSide)%(DATA_SIZE-1)][y] + //中心右侧
    data[x][(y+halfSide)%(DATA_SIZE-1)] + //中心下方
    数据[x][(y-halfSide+DATA_SIZE-1)%(DATA_SIZE-1)]; //高于中心
    

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

    【讨论】:

    • 此修复后结果看起来好多了。没有它,值会被“挤压”到边缘。
    【解决方案3】:

    对于任何人来说,这里是 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); 
            } 
        }
    
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      相关资源
      最近更新 更多