Pythonでライフゲームを実装!セルマトリクスの次の状態を求めるプログラム
問題の概要
2次元のバイナリ行列を考えます。「1」は生存しているセル(生きた細胞)、「0」は死んでいるセルを表します。あるセルの「近傍」とは、そのセルの上下左右および斜め方向に隣接する最大8個のセルのことです。
この記事では、以下のルールに従って行列全体の「次の状態」を求めるプログラムをPythonで実装します。このルールは、数学者ジョン・コンウェイが考案した有名な「ライフゲーム(Conway's Game of Life)」と同じものです。
セルの状態遷移ルール
- 生存しているセルは、隣接する生存セルが2つまたは3つの場合に限り、次の世代でも生存します。
- 死んでいるセルは、隣接する生存セルがちょうど3つの場合に新しいセルとして誕生します。
- 上記以外のすべてのセルは、過疎または過密により死滅します。
入力例と出力例
たとえば、次のような4×4の行列が入力として与えられたとします。
| 1 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 1 |
このとき、出力は次のようになります。右端の列の生存セルが消えているのが分かりますね。
| 1 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 |
| 1 | 1 | 0 | 0 |
解法アルゴリズム
この問題は、各セルについて近傍の生存セルの数を数え、ルールに従って次の状態を決定することで解けます。手順は以下の通りです。
- 行数を
n、列数をmとします。 - 結果を格納するための
n × mの行列resを作成し、すべて0で初期化します。 iを 0 から n-1 まで、jを 0 から m-1 まで二重ループで走査します。- 変数
sを 0 で初期化します(近傍の生存セル数の合計)。 - 現在のセルが死んでいる場合(matrix[i][j] == 0):
kを i-1 から i+1 まで、hを j-1 から j+1 までループし、行列の範囲内(0 ≤ k < n かつ 0 ≤ h < m)であればs += matrix[k][h]とします。s == 3のときだけres[i][j] = 1とします(誕生の条件)。
- 現在のセルが生存している場合:
- 同様にして近傍の合計
sを計算します。このとき自分自身(値が1)も合計に含まれる点に注意してください。 sが 3 または 4 のときres[i][j] = 1とします。自分自身の1を引くと近傍の生存セル数は 2 または 3 となり、生存条件を満たします。
- 同様にして近傍の合計
- 変数
- 最後に
resを返します。
なお、境界の外側を参照しないよう、インデックスの範囲チェック(0 ≤ k < n かつ 0 ≤ h < m)を必ず行うことが重要です。
Pythonでの実装例
それでは、上記のアルゴリズムを実際のコードで確認してみましょう。
class Solution:
def solve(self, matrix):
n, m = len(matrix), len(matrix[0])
res = [[0 for j in range(m)] for i in range(n)]
for i in range(n):
for j in range(m):
s = 0
if matrix[i][j] == 0:
# 死んだセル:近傍の生存セルをカウント
for k in range(i - 1, i + 2):
for h in range(j - 1, j + 2):
if 0 <= k < n and 0 <= h < m:
s += matrix[k][h]
# 近傍がちょうど3つなら誕生
res[i][j] = [0, 1][s == 3]
else:
# 生きたセル:自分自身も含めてカウント
for k in range(i - 1, i + 2):
for h in range(j - 1, j + 2):
if 0 <= k < n and 0 <= h < m:
s += matrix[k][h]
# 自分自身の1を含むため、sが3または4なら生存
if s in [3, 4]:
res[i][j] = 1
return res
ob = Solution()
matrix = [
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 1],
[1, 1, 0, 1]
]
print(ob.solve(matrix))
入力
[[1, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 1], [1, 1, 0, 1]]
出力
[[1, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0]]
コードのポイント
[0, 1][s == 3]は、条件式s == 3が True(=1)/ False(=0)になる性質を利用した簡潔な書き方です。可読性を重視するならres[i][j] = 1 if s == 3 else 0としてもよいでしょう。range(i - 1, i + 2)は i-1、i、i+1 の3つの値を生成するため、3×3の近傍領域を効率よく走査できます。- 生存セルの判定で
s in [3, 4]としているのは、合計値に自分自身(1)が含まれるためです。4から1を引いた3、3から1を引いた2が、それぞれ近傍の生存セル数に対応します。
計算量
- 時間計算量: O(n × m)。各セルにつき最大9マス(自分+近傍8つ)を参照します。
- 空間計算量: O(n × m)。結果を保存するための新しい行列が必要です。
このように、ライフゲームの次世代計算はシンプルな二重ループと近傍カウントだけで実装できます。ぜひご自身の環境でも動かしてみてください。
-
Pythonで多角形の外周(周囲長)を求めるプログラム
問題の概要2次元平面上にある単純な多角形(自己交差しないポリゴン)の頂点が、順序付きの点のリストとして与えられているとします。このとき、その多角形の外周(周囲長)を求めることが目的です。例として、入力が points = [(0, 0), (0,5), (3, 5), (3,0)] の場合を考えてみましょう。このときの出力は 16 になります。これは、図からも分かるように、長さ3の辺が2本、長さ5の辺が2本存在するためです。したがって、2×5 + 2×3 = 16 となります。アルゴリズムの考え方この問題は、「隣接する2つの頂点間の距離をすべて計算して合計する」というシンプルなアプローチで解く
-
Pythonで行列の転置を求めるプログラム
この記事では、与えられた問題に対する解法とアプローチについて詳しく解説します。 問題文 ある行列が与えられたとき、その転置を同じ行列に格納し、結果を表示する必要があります。 行列の転置とは、行を列に、列を行に入れ替えたものです。言い換えれば、行列Aの転置は、要素A[i][j]をA[j][i]と入れ替えることで得られます。 実装例 N = 4 def transpose(A): for i in range(N): for j in range(i+1, N): A[i][j], A[j][i] = A[j][i], A[i][j] # ドライ