2017-09-13 11 views
1

私はPythonでOOPを学んでいます。だから少し楽しみのために、私は今朝、GameOfLifeシミュレータを立ち上げました。起動時には、1秒間に約20サイクル(plt.pause(0.05)が追加されているため)に実行されますが、数秒以内に1秒あたり〜2サイクルまで減速します。私のGame of Lifeシミュレーションが数秒でクロールに遅くなるのはなぜですか?非難するMatplotlib?

私はそれがアルゴリズムそのものであるとは想像もできません。メモリリークの原因が分かりません。

matplotlibは古いプロットをダンプすることができませんが、何千ものオーバーレイされた画像が蓄積されていますか?私はdel imを追加しようとしましたが、それは役に立たなかった...

これは重要ではありませんが、私は答えから何かを学ぶことができたと感じています。

PS私の実装が貧弱だと思うなら、私に教えてください、私は学びたいです!

import matplotlib.pyplot as plt 
import numpy as np 
from scipy.ndimage import convolve 
import time 

class GoL(): 

    KERNEL = np.array([[1, 1, 1], 
         [1, 0, 1], 
         [1, 1, 1]]) 

    def __init__(self, width, height, p=0.3): 
     self.width = width 
     self.height = height 
     self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p]) 

    def play(self): 
     self.plot() 
     while True: 
      self.cycle() 

    def cycle(self): 
     c = self.eval() 
     for x in range(1,self.width-1): 
      for y in range(1,self.height-1): 
       c_val = c[x,y] 
       if c_val == 3: self.matrix[x,y] = 1 #Alive regardless of whether cell alive or dead 
       elif c_val < 2: self.matrix[x,y] = 0 #Dead regardless of whether cell alive or dead 
       elif c_val > 3: self.matrix[x,y] = 0 #Dead regardless of whether cell alive or dead 
       elif self.matrix[x,y] == 1 and c_val == 2: self.matrix[x,y] = 1 #If a living cell with 2 neighours, live 
       else: self.matrix[x,y] = 0 #Only other option is dead with 2 neighbours; die 
     self.plot() 

    def eval(self): 
     c = convolve(self.matrix, GoL.KERNEL, mode='constant') 
     return c 

    def plot(self): 
     im = plt.imshow(self.matrix) 
     plt.pause(0.05) 
     del im #Added to see if speeds things up; it does not 

して実行する:

gol = GoL(width=100,height=100) 
gol.play() 
+1

見つけるための最善の方法は、コードをプロファイリングすることです。 matplotlibs 'ion'や' 'animation''(https://matplotlib.org/api/animation_api.html)を使用していますか? – MSeifert

答えて

1

あなたはこの数は、削除にもかかわらず、着実に増加しますあなたの場合は

print (len(plt.gca().images)) 

、以前の呼び出しの画像がplot関数内の画像の数を印刷することにより、まだ存在していることがわかりますこれはまだそれが軸の一部であるため、繰り返しごとに再描画されるためです。

イメージを1回だけ描​​画してからデータを更新するほうがずっと良いでしょう。

class GoL(): 
    # ... 

    def __init__(self, width, height, p=0.3): 
     self.width = width 
     self.height = height 
     self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p]) 
     self.im = plt.imshow(self.matrix) 

    # .... 

    def plot(self): 
     self.im.set_data(self.matrix) 
     plt.pause(0.05) 
     print len(plt.gca().images) 

これにより、一定の速度でアニメーションが作成されます。これは、はるかに安定しており、あなたがsavelyアニメーションを終了することができますよう、

  1. 使用matplotlib.animation.FuncAnimation:コードを改善する場所の問題については


    、二つのことがあります。
  2. マトリックスのすべてのピクセルをループするのではなく、numpy配列の条件を使用します。

全コード:

import matplotlib.pyplot as plt 
import numpy as np 
from scipy.ndimage import convolve 
import matplotlib.animation as animation 

class GoL(): 

    KERNEL = np.array([[1, 1, 1], 
         [1, 0, 1], 
         [1, 1, 1]]) 

    def __init__(self, width, height, p=0.3): 
     self.width = width 
     self.height = height 
     self.matrix = np.random.choice(a=[1, 0], size=(width, height), p=[p, 1 - p]) 
     self.im = plt.imshow(self.matrix) 

    def play(self): 
     self.plot() 
     self.ani= animation.FuncAnimation(plt.gcf(), self.cycle, repeat=True, interval=50) 
     plt.show() 

    def cycle(self, n): 
     c = self.eval() 
     new = np.zeros_like(self.matrix) 
     new[c == 3] = 1 
     new[c < 2] = 0 
     new[c > 3] = 0 
     new[(self.matrix == 1) & (c == 2)] = 1 
     self.matrix = new 
     self.plot() 

    def eval(self): 
     return convolve(self.matrix, self.KERNEL, mode='constant') 

    def plot(self): 
     self.im.set_data(self.matrix) 

gol = GoL(width=100,height=100) 
gol.play() 
+0

私は、より最適化され、より速く実行できるソリューションを追加しました。 – ImportanceOfBeingErnest

+1

恐ろしい!ありがとうございました。そして、あなたが0の新しい配列を作ったので、 'new [c <2] = 0'と' new [c> 3] = 0'という行を削除することができます。 – James

1

をキャンバスを再利用する前に、古い数字をクリアする必要があります。あなたは現在のフィギュア(http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.clf)をクリアするmatplotlib.pyplot.clf()を使用することができます。

def plot(self): 
    plt.clf() # Clear the old figure 
    im = plt.imshow(self.matrix) 
    plt.pause(0.05) 

は、この情報がお役に立てば幸い! :)

+0

ありがとう、非常にまっすぐ! – James

関連する問題