【问题标题】:Thoughts and question when implementing animation plot of Collatz Conjecture (3X+1)实现柯拉兹猜想(3X+1)动画情节时的思考与问题
【发布时间】:2021-10-15 14:09:42
【问题描述】:

感谢 Veritasium 关于该主题的精彩视频,我正计划快速复制他在视频中显示的动画,其中数字上下跳动直到达到数字 1,具体取决于初始数字。

下面我想出了一个实现动画的代码版本。但是我在构建代码时有一个疑问和困惑。

我发现如果我不将 y-data 初始化为 y=np.empty(100) 而是使用空列表,则会抛出错误 list assignment index out of range

所以我很困惑为什么我不能从一个空列表开始,因为我知道,取决于y_start 的值,len(y) 会有所不同。如果我可以将这些计算出的 y 值收集到一个列表中(稍后将它们转换为数组),那么我不必去整个夜场设置plt.xlim(1,100)(相反,我可以设置plt.xlim(0,len(y))也是由于最终 y 数据中可能剩余 0.0 值,我必须在 if 语句中添加附加条件(and 之后的第二个)-> if y[i] % 2 == 0 and y[i] != 0: 否则,即使在 y 达到值 1 后它也会变得混乱......

此外,我想添加 y-data 的值显示在每个单独的点之上,但我不知道如何在代码中实现它......如果有人能帮助解决这个问题,我将不胜感激让动画看起来更“完整”!

这是我测试过的代码

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()

所以,我的问题是,

为什么从一个空列表开始包含计算的 y 会失败?

另外,在早期版本中,在 collatz 函数定义中,我的代码是这样的:

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

编译卡住了,代码好像在无限循环……不知道是不是while statement的用法?我应该怎么做/添加来纠正它?

【问题讨论】:

  • for循环中的while可能会使整个事情崩溃?让我们循环一下:i = 0 => y[0] = 33,你到达了odd() 函数。我猜你永远在while 中循环?因为i 没有增加(也不应该手动)。删除while 可能会解决您的问题?
  • 顺便说一句,可能还有第二个问题。如果您的循环到达i = 99,您将查找y 的索引100,这也会产生错误
  • 是的,While 循环对我来说总是一件棘手的事情......这就是为什么在上面有效的代码中,我删除了整个 while loop 感觉好像有更好/更智能的方法定义用于计算的 Collat​​z 函数以及构建 y 数组...

标签: python matplotlib animation


【解决方案1】:

我已经重写了你的代码(在我的机器上工作),并会尝试回答你的问题

您不能从y 的空列表开始,因为collatz() 函数需要一个起点。因此,如果y 为空,则没有可开始的内容,函数将失败。在下面的新代码中,我向您的函数添加了一个参数start(在代码中:49)。现在这是您的功能的新起点。请注意,如果您想要一个随机起点而不是您定义的起点,您可以删除 start,并将 y = [start] 替换为 y = [int(np.random.randint(1, 100, 1))] 或其他绘制随机整数的代码。

现在 collat​​z 使用了一个 while 循环:只要 y 大于 1,它就会工作(因此对于 y = 0 或 1,它将停止)。请注意,-1 运算符表示“添加到 y 的最后一个元素”。对于每个号码,它执行even()odd() 函数,然后使用append 将号码添加到列表中。这确保了列表只需要它需要的长度。请注意,在这种情况下,while 循环是最佳选择,因为您不知道循环将持续多长时间。当你有固定的迭代次数时,应该选择一个 for 循环。

最后根据y的长度确定x。

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()

【讨论】:

  • While y[-1] > 1: 这是天才!!!非常感谢!
  • 其实我想在动画进行的时候把单独的 y 值添加到情节中,你知道怎么做吗?
  • @Maggie 你到底是什么意思,想要的结果是什么?
  • 我在想,在这个显示 y 数据上下弹跳的动画图之上,同时,如果相应的 y 值也可以显示在该点上方,那就太好了。这在动画情节中可行吗?
  • 应该是,这类似于this question here。你可以试试这个,如果你被卡住了,你可以问一个关于 SO 的新问题(如果你愿意,可以随时标记我)
猜你喜欢
  • 2021-11-22
  • 2019-01-03
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 2011-10-14
相关资源
最近更新 更多