Search code examples
pythonarraysnumpyrecursionfractals

How to Zoom in Recursively on a Numpy Array


I'm trying to show that an array I've generated looks self-similar on multiple length scales. To do this I want to write a function that prints the top left-hand corner of an input array, then prints the top left-hand corner of that section, and so on for a specified number of iterations. I wrote this recursive function. However, the output isn't what I expected.

def zoom(array, max_zoom, zoom_level):
    half_width = int(array.shape[0]/2)
    half_height = int(array.shape[1]/2)
    print(array)

    while zoom_level < max_zoom:
        array = array[:half_width, :half_height]
        zoom_level += 1
        zoom(array, max_zoom, zoom_level)

The problem is instead of printing to the required level and then stopping, the array begins to get larger again and the function halts after a few additional iterations.

For example zoom(array, 3, 0) should give the output below for an 8x8 grid.

[[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]] 

[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]

[[0. 0.]
[0. 0.]]

[[0.]]

But instead gives:

[[0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]]
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
[[0. 0.]
 [0. 0.]]
[[0.]]
[[0. 0.]
 [0. 0.]]
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
[[0. 0.]
 [0. 0.]]
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

Can anyone shine any light on why this function is misbehaving?


Solution

  • The problem is having a while in your recursive function. What you're doing is that you're repeating the same process for each recursive call, and by that you're printing unnecessary result.

    def zoom(array, max_zoom, zoom_level):
        print(array)
        if zoom_level < max_zoom:
            half_width = array.shape[0] // 2
            half_height = array.shape[1] // 2
            array = array[:half_width, :half_height]
            zoom_level += 1
            zoom(array, max_zoom, zoom_level)
    
    A = np.zeros((9, 9))
    
    zoom(A, 3, 0)
    

    The output will be:

    [[0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]
     [0. 0. 0. 0. 0. 0. 0. 0. 0.]]
    [[0. 0. 0. 0.]
     [0. 0. 0. 0.]
     [0. 0. 0. 0.]
     [0. 0. 0. 0.]]
    [[0. 0.]
     [0. 0.]]
    [[0.]]