【ITニュース解説】Building a Row Echelon Form Checker in Python and Go with Tests
2025年09月23日に「Dev.to」が公開したITニュース「Building a Row Echelon Form Checker in Python and Go with Tests」について初心者にもわかりやすく解説しています。
ITニュース概要
階段行列は線形代数で行列を簡略化する重要な形だ。この記事では、全てのゼロ行が下部にあり、各行の最初の非ゼロ要素(ピボット)が右へ進むというルールに基づき、行列が階段行列かを判定する関数をPythonとGoで実装し、そのテスト方法を解説する。
ITニュース解説
Row Echelon Form(行階段形)とは、線形代数という数学の分野で使われる、行列を特定の形に整理する方法だ。この形にすることで、連立方程式を解く際など、複雑な行列計算が簡単になる。
今回解説する記事では、与えられた行列が行階段形になっているかどうかをチェックするプログラムを、PythonとGoという二つのプログラミング言語で作る方法を紹介している。ここでチェックする行階段形には、主に二つの重要なルールがある。
一つ目のルールは、「すべての要素がゼロである行(ゼロ行)は、行列の一番下に集められるべきだ」というものだ。もし、一つでもゼロではない要素を含む行(非ゼロ行)よりも上にゼロ行があった場合、それは行階段形とはみなされない。たとえば、「ゼロ行、非ゼロ行、非ゼロ行」という順序では不正であり、ゼロ行を一番下に移動させる必要がある。
二つ目のルールは、「各行の最初の非ゼロ要素(これを『ピボット』と呼ぶ)は、その一つ上の行のピボットよりも必ず右側に位置していなければならない」というものだ。言い換えれば、各行で初めてゼロではない数字が現れる位置が、行を下に進むにつれてどんどん右にずれていく、ということだ。もし、ある行のピボットが、上の行のピボットよりも左か同じ位置にあった場合、行階段形ではないと判断される。記事の例で示されている「1, 2, 3」「0, 1, 4」「0, 0, 1」のような行列では、各行のピボットは左から1番目、2番目、3番目の列にあり、右にずれているため、このルールを満たしている。
また、行階段形には「ピボットが必ず1であること」や「ピボットより上の要素がすべてゼロであること」といった、より厳密なルールを持つ「簡約化された行階段形(Reduced Row Echelon Form)」というものもあるが、今回のチェッカーはそこまで厳しくなく、上記の二つの主要ルールのみを確認する。これにより、基本的な行階段形の概念をしっかりと理解し、実装することに焦点を当てている。
Pythonでの実装では、行列を「リストの中にリストが入った形」として表現する。たとえば、[[1, 2, 3], [0, 1, 4], [0, 0, 1]]のように記述する。is_row_echelon_formという関数を作成し、この関数が与えられた行列が行階段形であるかを真偽値(TrueまたはFalse)で返すようにする。この関数は、まず行列が空であるか、または行の長さが不揃いではないかといった基本的なチェックを行う。空の行列であれば自明に行階段形とみなしTrueを返す。
次に、この関数は行列の各行を上から順に調べていく。その際、「last_pivot_col」という変数で、一つ上の行のピボットがあった列の位置を覚えておく。「seen_zero_row」という変数では、これまでにゼロ行を見たかどうかを記録する。現在の行でピボット(最初の非ゼロ要素)を見つけたら、そのピボットがlast_pivot_colよりも右にあるかどうかを確認する。もし右になければ、二つ目のルールに違反しているため、Falseを返す。また、もし現在の行がゼロ行でなく、かつ既にseen_zero_rowがTrue(つまり、これまでにゼロ行を見た)状態であれば、一つ目のルールに違反しているため、これもFalseを返す。これらのチェックをすべての行に対して行い、どのルールにも違反しなければTrueを返すという流れになっている。
プログラムが正しく動作するかを確認するために、「テスト」は非常に重要だ。記事ではPythonのunittestというモジュールを使ってテストコードを書く方法を紹介している。テストでは、行階段形として正しい行列、ピボットの位置が間違っている行列、ゼロ行が誤った位置にある行列、すべての要素がゼロの行列、空の行列、単一行の行列、さらには行の長さが不揃いな行列など、さまざまな種類の行列を与えて、is_row_echelon_form関数が期待通りの結果(TrueまたはFalse)を返すかを確認する。例えば、正しい行列を与えてTrueが返ることを確認し、ピボットが正しくない行列を与えてFalseが返ることを確認するといった具合だ。これらのテストケースを網羅的に用意することで、プログラムの信頼性を高めることができる。
次に、Go言語での実装だが、基本的なロジックはPythonと非常に似ている。Goでは、行列を「スライスのスライス」([][]intのように表記)として表現する。これはPythonのリストのリストと概念は同じだが、Goはデータ型を厳密に扱う言語であるため、例えば整数型のみを格納する行列であることを明示的に宣言する。関数名もPythonのis_row_echelon_formに対して、GoではisRowEchelonFormという命名規則に従う。Pythonと同様に、空の行列や行の長さの不揃いをチェックし、lastPivotColとseenZeroRowの変数を使って二つの主要ルールを検証していく。コードの構造やループ処理はPython版とほぼ同じなので、Pythonのコードを理解していればGo版もスムーズに理解できるだろう。
Go言語でのテストも、Pythonと同様にtestingパッケージを使って行われる。Goでは「テーブル駆動型テスト」というスタイルがよく用いられる。これは、テストケースとその期待される結果を一つの構造体(struct)のスライス(リストのようなもの)としてまとめて定義する方法だ。"valid_ref"、"invalid_pivot"、"misplaced_zero"といった名前を付けたテストケースを列挙し、それぞれのケースに対して行列のデータと、その行列が期待する真偽値(want)を設定する。そして、これらのテストケースを一つずつループで回して、isRowE echelonForm関数が期待通りの結果を返すかどうかを検証する。結果が異なれば、エラーメッセージを出力する。この方法だと、新しいテストケースを追加するのが非常に簡単になるため、効率的にテストを行う際に役立つ。
PythonとGoの実装を比較すると、どちらも同じ論理で動いているが、いくつかの違いがある。Pythonは動的な型付けが特徴で、柔軟なコードが書け、プロトタイピングが速いという利点がある。一方、Goは静的な型付けのため厳密なコードが書け、コンパイル言語であるため、大規模な行列を扱う場合など、パフォーマンス面で優位に立つことが多い。エラーチェックに関しても、どちらの言語も実行時に行の長さの不揃いを確認している。テストの方法も、Pythonはunittestクラスベース、Goはテーブル駆動型と、それぞれの言語の慣習に従っている。
この行階段形チェッカーは、非正方行列(行と列の数が異なる行列)や、ピボットの前にゼロが並ぶ行列(例として[[0,1,2],[0,0,3]]のような行列も行階段形として有効だ)といったエッジケースにも対応できるように設計されている。浮動小数点数を扱う場合には微小な誤差を考慮する必要があるが、今回の実装は整数に特化している。このチェッカーの計算効率は、行列の行数と列数を掛け合わせた値に比例する(O(rows * cols))ため、ほとんどの用途で十分な性能を発揮する。このようなチェッカーを自分で作ってみることは、線形代数の基礎を深く理解するだけでなく、異なるプログラミング言語で同じロジックを実装するスキルを向上させる上でも非常に有益だ。そして、将来的にこのチェッカーを拡張して、より厳密な簡約化された行階段形をチェックしたりすることも可能になるだろう。システム開発において、このように基礎となる要素をしっかりとテストし、その動作を検証する習慣は、信頼性の高いシステムを構築するために不可欠である。