1

Thanks to Veritasium great video about the topic, I was planning to do a quick replication of the animation he showed in the video where the number bouncing up and down until hit the number 1, depending on the initial number.

Below I figured out a version of code to implement the animation. But I have a question and confusion while constructing the code.

I found that if I don't initialize the y-data as y=np.empty(100) instead with a empty list, it will throw an error list assignment index out of range

So I'm very confused why I can't start with a empty list, because I know, depending on the value of y_start the len(y) varies. If I can collect those calculated y value into a list (converting them into array later) then I don't have to go the whole night-yard setting plt.xlim(1,100) (instead, I could just set plt.xlim(0,len(y)) also due to the potential remaining 0.0 value in the final y-data, I have to add additional condition (2nd after and) in the if statement -> if y[i] % 2 == 0 and y[i] != 0: Otherwise, it goes haywire even after y reached the value 1....

In addition, I want to add y-data's value displaying on top of the each individual point, but I have no clue how to implement that in the code...It would be greatly appreciate if anyone can help on this issue to make the animation looks more "complete"!

Here is the code that I've tested

import numpy as np
from matplotlib.animation import FuncAnimation
from matplotlib import pyplot as plt

def odd(num):
    return (num*3)+1

def even(num):
    return num // 2

y_start = 33
x = np.linspace(0, 100, 100)
# y = []
def collatz():
    y = np.empty(100)
    y[0] = y_start
    for i in range(0,100):
            if y[i] % 2 == 0 and y[i] != 0:
                y[i+1] = even(y[i])
            else:
                y[i+1] = odd(y[i])
            if y[i+1] == 1:
                break
    return y

y = collatz()

fig = plt.figure()
plt.xlim(1,100)
plt.ylim(1,max(y))

draw, = plt.plot([],[])
def update(idx):
    draw.set_data(x[:idx], y[:idx])
    return draw,

a = FuncAnimation(fig, update, frames=len(x), interval=90)
plt.show()

So again, my questions are,

why starting with an empty list to contain calculated y fails?

Also, in the early version, inside the collatz function definition, my code was like:

def collatz():
    y = np.empty(100)
    y[0] = y_start
    for i in range(0,100):
        while y[i] != 1:
            if y[i] % 2 == 0 and y[i] != 0:
                y[i+1] = even(y[i])
            else:
                y[i+1] = odd(y[i])
        if y[i+1] == 1:
            break
    return y

The compilation freezes, the code seems undergoes an infinite cycle...I don't know, is it the usage of the while statement ? what should I do/add to correct it?

Maggie
  • 149
  • 2
  • 9
  • The while in the for loop might make the whole thing collapse ? Let's loop a bit: ```i = 0``` => ```y[0] = 33```, you reach the ```odd()``` function. And you loop for ever in the ```while``` i guess ? As ```i``` is not incremented (and shouldn't be manually). Removing the ```while``` may resolve your problem ? – Metapod Aug 12 '21 at 07:55
  • There might be a second problem by the way. If your loop reached ```i = 99```, you will look for the index 100 of ```y```, which should generate an error too – Metapod Aug 12 '21 at 08:01
  • Yeah, the `While` loop is always a tricky thing for me...that's why in the above code that works, I remove the whole `while loop` feels like there's a better/smarter way to define the Collatz function for calculation as well as constructing the y-array... – Maggie Aug 12 '21 at 08:08

1 Answers1

1

I have rewritten your code (works on my machine), and will try to answer your questions

You cannot start from an empty list for y because the collatz() function needs a starting point. Hence, if y is empty, there is nothing to start from and the function fails. In the new code below I have added a parameter start to your function (in the code: 49). This is now the new starting point of your function. Note that if you want a random starting point instead of one defined by you, you can delete start, and replace y = [start] with y = [int(np.random.randint(1, 100, 1))] or another code that draws a random integer.

Now collatz uses a while loop: it works as long as y is larger than 1 (hence for y = 0 or 1 it will stop). Note that the -1 operator means 'the last element added to y'. For each number it does the even() or odd() function, and then it adds the number to the list using append. This ensures that the list is only as long as it needs to be. Note that in this case a while loop is the best option since you don't know how long the loop will last. When you have a fixed amount of iterations, a for loop should be chosen.

Finally, x is determined based on the length of y.

from matplotlib.animation import FuncAnimation
from matplotlib import pyplot as plt

def odd(num):
    return (num*3)+1

def even(num):
    return num // 2

def collatz(start):
    y = [start]
    while y[-1] > 1:
            if y[-1] % 2 == 0:
                y.append(even(y[-1]))
            else:
                y.append(odd(y[-1]))

    return y

y = collatz(49)
x = list(range(len(y)))

fig = plt.figure()
plt.xlim(1,len(y))
plt.ylim(1,max(y))

draw, = plt.plot([],[])
def update(idx):
    draw.set_data(x[:idx], y[:idx])
    return draw

a = FuncAnimation(fig, update, frames=len(x), interval=90)
plt.show()
phuycke
  • 346
  • 2
  • 10
  • 1
    `While y[-1] > 1:` This is genius!!! Thanks so much! – Maggie Aug 12 '21 at 08:39
  • Actually I was thinking to add the individual y values to the plot while the animation goes on, do you know how to do that? – Maggie Aug 12 '21 at 09:52
  • @Maggie what do you mean exactly, what is the desired result? – phuycke Aug 12 '21 at 10:03
  • I was thinking, on top of this animated plot showing y-data bouncing up and down, meanwhile, it would be great if the corresponding y-value can also be displayed above the point. Is that doable in an animation plot? – Maggie Aug 12 '21 at 10:05
  • it would be, this is similar to [this question here](https://stackoverflow.com/questions/6282058/writing-numerical-values-on-the-plot-with-matplotlib). You can try this, and if you are stuck you can ask a new question on SO (and feel free to tag me if you want) – phuycke Aug 12 '21 at 11:26