Python Diamond Square算法实现

时间:2016-01-02 17:12:14

标签: python algorithm

我尝试创建一个基于this javascript的菱形方块算法,因为它是可读的并且对我来说也很有意义。我遇到了一些我似乎无法解决的问题。

运行代码时,所需的输出是填充2D阵列的每个位置的一些随机值,该位置与初始阵列初始化不同。我遇到的问题是结果2D数组没有完全填充,当网格大小增加到3以上时,我得到IndexError: list index out of range错误。

以下是代码:

class DiamondSquare:

    def __init__(self, size, roughness):

        self.size = (size ** 2) + 1
        self.max = self.size - 1
        self.roughness = roughness
        self.grid = self.make_grid(self.size)
        self.divide(self.max)
        print(self.grid)

    def divide(self, size):

        x = size / 2
        y = size / 2
        half = size / 2
        scale = self.roughness * size

        if (half < 1):
            return

        # Squares
        for y in range(half, self.max, size):
            for x in range(half, self.max, size):
                s_scale = random.uniform(0, 1) * scale * 2 - scale
                self.square(x, y, half, s_scale)

        # Diamonds
        for y in range(0, self.max, half):
            for x in range((y + half) % size, self.max, size):
                d_scale = random.uniform(0, 1) * scale * 2 - scale
                self.diamond(x, y, half, d_scale)

        self.divide(size / 2)

    def square(self, x, y, size, scale):

        """
            TL      TR

                X       

            BL      BR
        """

        tl = self.grid[x - size][y - size]
        tr = self.grid[x + size][y - size]
        bl = self.grid[x + size][y + size]
        br = self.grid[x - size][y + size]

        average = ((tl + tr + bl + br) / 4) + scale
        self.grid[x][y] = average

    def diamond(self, x, y, size, scale):

        """
                T

            L   X   R

                B
        """

        t = self.grid[x][y - size]
        r = self.grid[x + size][y]
        b = self.grid[x][y + size]
        l = self.grid[x - size][y + size]

        average = ((t + r + b + l) / 4) + scale
        self.grid[x][y] = average

    def make_grid(self, size):

        grid = []
        for y in range(size):
            temp = []
            for x in range(size):
                temp.append(-1)
            grid.append(temp)

        grid[0][0] = self.max
        grid[self.max][0] = self.max / 2
        grid[self.max][self.max] = 0
        grid[0][self.max] = self.max / 2

        return grid

    def get_grid(self):
        return self.grid

当尝试将size变量(在init中)增加到大于2的值时,我得到以下回溯:

Traceback (most recent call last):
  File "C:\Users\user\Documents\Development\Python\Fractal\diamond_square.py", line 150, in <module>
    a = DiamondSquare(5, 0.7)
  File "C:\Users\user\Documents\Development\Python\Fractal\diamond_square.py", line 14, in __init__
    self.divide(self.max)
  File "C:\Users\user\Documents\Development\Python\Fractal\diamond_square.py", line 35, in divide
    self.diamond(x, y, half, d_scale)
  File "C:\Users\user\Documents\Development\Python\Fractal\diamond_square.py", line 68, in diamond
    r = self.grid[x + size][y]
IndexError: list index out of range

老实说,我不确定为什么会这样,我根本无法理解。我使用以下代码来产生此错误:

a = DiamondSquare(x, 0.7)

其中x是大于2的任何整数,第二个参数是粗糙度。

关于网格错误,尝试从DiamondSquare.divide()创建网格,产生以下内容:

[[4, 1.0649105908291359, 1.234026481506731, 0.07818244918327344, 2], 
[0.43855022217756057, 0.4659935454877355, 1.283183468707215, 0.28019876872734906, -1], 
[-0.4946413746345607, -1.1327574166276582, 0.45804405178511276, -1.4905717022572778, -1], 
[-1.4175095415414622, -0.660055583070249, -0.8017056243549873, -0.18216161649389495, -1], 
[2, -1, -1, -1, 0]]

在-1的位置,应该有其他随机数与网格的其余部分一样。 -1表示底部和右侧的中点。我相信这与我的钻石循环有关,但我不确定我哪里出错了。

我实现了这个网格,我使用以下代码:

a = DiamondSquare(2, 0.7)

其中第一个参数是大小,第二个参数是粗糙度。

我可以帮助解决上述问题吗?提前谢谢!

2 个答案:

答案 0 :(得分:4)

创建尺寸参数时,您更改了底座和电源的位置。您撰写了(size ** 2) + 1,但应该是(2 ** size) + 1。这有望解决您的问题。

答案 1 :(得分:1)

我设法通过删除2D列表并简单地使用1D列表来解决这个问题,这是我本来应该做的事情,但我没有,因为我误读了原始代码。

class DiamondSquare:

    def __init__(self, size, roughness):

        self.size = (2 ** size) + 1
        self.max = self.size - 1
        self.roughness = roughness
        self.make_grid(self.size)
        self.divide(self.max)

    # Sets x,y position in self.grid
    def set(self, x, y, val):
        self.grid[x + self.size * y] = val;

    # Get's value of x, y in self.grid
    def get(self, x, y):
        if (x < 0 or x > self.max or y < 0 or y > self.max):
            return -1
        return self.grid[x + self.size * y]

    def divide(self, size):

        x = size / 2
        y = size / 2
        half = size / 2
        scale = self.roughness * size

        if (half < 1):
            return

        # Square
        for y in range(half, self.max, size):
            for x in range(half, self.max, size):
                s_scale = random.uniform(0, 1) * scale * 2 - scale
                self.square(x, y, half, s_scale)

        # Diamond
        for y in range(0, self.max + 1, half):
            for x in range((y + half) % size, self.max + 1, size):
                d_scale = random.uniform(0, 1) * scale * 2 - scale
                self.diamond(x, y, half, d_scale)

        self.divide(size / 2) 

    def square(self, x, y, size, scale):

        top_left = self.get(x - size, y - size)
        top_right = self.get(x + size, y - size)
        bottom_left = self.get(x + size, y + size)
        bottom_right = self.get(x - size, y + size)

        average = ((top_left + top_right + bottom_left + bottom_right) / 4)
        self.set(x, y, average + scale)

    def diamond(self, x, y, size, scale):

        """
                T

            L   X   R

                B
        """

        top = self.get(x, y - size)
        right = self.get(x + size, y)
        bottom = self.get(x, y + size)
        left = self.get(x - size, y)

        average = ((top + right + bottom + left) / 4)
        self.set(x, y, average + scale)



    def make_grid(self, size):

        self.grid = []

        for x in range(size * size):
            self.grid.append(-1)

        self.set(0, 0, self.max)
        self.set(self.max, 0, self.max /2 )
        self.set(self.max, self.max, 0)
        self.set(0, self.max, self.max / 2)

    def get_grid(self):
        return self.grid

a = DiamondSquare(3, 0.5)
print(a.get_grid())