14. 多項式クラス(7)
多項式クラスのデータ構造は、
多項式クラス(1)に書いたように、
変数 -> string x -> "x"
変数の累乗 -> (string,int) x3 -> ("x",3)
項 -> (map<string,int>,double) 2x3y -> ((("x",3),("y",1)),2)
多項式 -> map<map<string,int>,double> x+y -> ((("x",1)),1),((("y",1)),1)
係数は double だったんですね。
これを作ろうとした経緯からしてそれは必然だったんです。
しかし、ここでは常に整数として考えましょう
(Template を使うという手もあるが)。
以上は余談で、このデータ構造だとあまりに処理が重くなりそうなので、
因数分解する前に、もっと処理しやすくなるようなデータ構造に変換しましょう。
ところで、項の数は最大いくつでしょう。
例えば、3変数3次の場合、一般の項は
xkylzm k + l + m <= 3
と表せるので、項数は、
k + l + m <= 3
が成り立つ非負整数(k, l, m)の解の個数です。これは、
○○○○○○
のうち3つを黒丸に塗る組合せと同じです。例えば、
○●●○○● -> (1, 0, 2) -> xz2
○●○●●○ -> (1, 1, 0) -> xy
3つの黒丸がx、y、z、余りの4つの領域を分けるしきいと解釈します。
よって項の数は、
6C3 = 20
となります。一般的にm変数n次なら、
m+nCm
しかるに、データ構造は次のようにすればよいです。
int a[20];
ただし、これでは何番目の項がどういう項かというのをすぐに分からないので、
裏でその表も持っておきましょう。
int b[20][3];
この表を作るのに時間のことは考えなくていいので、
分かりやすいアルゴリズムを考えましょう。
3変数3次のときは次のようになります。
0 0 0
1 0 0
2 0 0
3 0 0
0 1 0
...
この列を作り出すアルゴリズムは次のようになります。
1) 最初は 0 0 0
2) 全ての成分を足して3かどうか調べる
3) 3でなかったら先頭の成分に1をたす
4) 3だったら0でない最初の成分を探す
5) それが最後の成分なら終わり
6) そうでなかったらその成分を0にし、その次の成分に1をたす
このアルゴリズムで動く JavaScript を作ってみました。
変数の数と次数を入力してボタンを押してください。
全ての項が表示されます。
100項より多くなる場合は表示されません。
高校生にはたぶんおなじみの次の多項式の因数分解を考えてみましょう。
f(x,y,z) = x3 + y3 + z3 - 3xyz
これは3次式なので因数分解できるとすれば1次式と2次式の因数分解になります。
その一次式を
g(x,y,z) = a0 + a1x + a2y + a3z
とすると、
f(0,0,0) = g(0,0,0)h(0,0,0) = 0
f(1,0,0) = g(1,0,0)h(1,0,0) = 1
f(2,0,0) = g(2,0,0)h(2,0,0) = 8
f(3,0,0) = g(3,0,0)h(3,0,0) = 27
f(0,1,0) = g(0,1,0)h(0,1,0) = 1
f(1,1,0) = g(1,1,0)h(1,1,0) = 2
f(2,1,0) = g(2,1,0)h(2,1,0) = 9
f(0,2,0) = g(0,2,0)h(0,2,0) = 8
f(1,2,0) = g(1,2,0)h(1,2,0) = 9
f(0,3,0) = g(0,3,0)h(0,3,0) = 27
f(0,0,1) = g(0,0,1)h(0,0,1) = 1
f(1,0,1) = g(1,0,1)h(1,0,1) = 2
f(2,0,1) = g(2,0,1)h(2,0,1) = 9
f(0,1,1) = g(0,1,1)h(0,1,1) = 2
f(1,1,1) = g(1,1,1)h(1,1,1) = 0
f(0,2,1) = g(0,2,1)h(0,2,1) = 9
f(0,0,2) = g(0,0,2)h(0,0,2) = 8
f(1,0,2) = g(1,0,2)h(1,0,2) = 9
f(0,1,2) = g(0,1,2)h(0,1,2) = 9
f(0,0,3) = g(0,0,3)h(0,0,3) = 27
の2、5、6番目から、
g(1,0,0) = a0 + a1 = 1, -1
g(0,1,0) = a0 + a2 = 1, -1
g(1,1,0) = a0 + a1 + a2 = 1, -1, 2, -2
ここで
a0 + a1 = 1
としてよいです。
a0 + a1 + a2 = 1, -1, -2
だと、他の2つを足したものからこれを引いて、どれを選択しても、
a0 = 0
となって、成り立たなくなります。よって、
a0 + a1 + a2 = 2
(a0, a1, a2) = (0, 1, 1)
となります。同様に、
a3 = 1
これで一次の項が
x + y + z
でなければならず、これで最初の式を割って、
x3 + y3 + z3 - 3xyz = (x + y + z)(x2 + y2 + z2 - yz - zx -xy)
となります。
しかし、一般化するにはまだハードルがありますね。
ところで、
x3 + y3 + z3 - 3xyz = (x + y + z)(x2 + y2 + z2 - yz - zx -xy)
を高校1年で習ったとき、変な式だなあと思いましたが、
この-3というのは次のような意味があります。
第2項は、
x2 + y2 + z2 - yz - zx -xy = (y - z)2 + (z - x)2 + (x - y)2
要するにこれが0なら、
x = y = z
を意味しています。だから、
x = y = z => x3 + y3 + z3 - 3xyz = 0
で-3でなければならないんですね。