正規分布の近似(11)

積分を含んだ多項式の漸化式を計算するためのプログラムを書く準備をする。
プログラムはお手軽にJavaScriptで書く。
要するにここでは、多項式のユーザ定義オブジェクトのコードを書く。

プロパティ

多項式の係数を配列として持つ。


function poly() {
this.a = [ ];
}

コンストラク

というべきかわからないが、
3種類用意する。
といってもJavaScriptでオーバーライドなどできないはずなので、
引数を見て選択する。


var p = new poly();
var q = new poly(1, 2, 3);
var r = new poly([ 1, 2, 3 ]);

最初のは、多項式0を生成する。
次は、多項式1+2x+3x2を生成する。
最後のも同じだが、配列を引数としている。

コピー

新たに同じ値のオブジェクトを生成して、リファレンスを返す。


var q = p.copy();

加減乗

これも新たなオブジェクトを生成して、リファレンスを返す。
数値を引数にしてもOK。


var r1 = p.add(q);
var r2 = p.add(1);
var r3 = p.substract(q);
var r4 = p.substract(1);
var r5 = p.multiply(q);
var r6 = p.multiply(2);

関数の値

引数に数値を取り、数値を返す。


var r = p.value(3);

合成

関数の合成。


var r = p.synthesize(q);

積分

引数無しなら不定積分、2つなら定積分
2つとも数値なら数値、そうでなければ多項式を返す。


var q = p.integral();
var r = p.integral(1, x);

文字列

a0 + a0x + a0x2 + ...
の形で返す。


var str = p.toString();
alert(p); // toStringは省略できる

ソースは隠しておく(10/25修正)。



function poly() {
var args = poly.arguments;
if(args.length == 0)
this.a = [ 0 ];
else {
this.a = [ ];
for(var i = 0; i < args.length; i++) {
var b = args[i];
if(typeof b == "number")
this.a.push(b);
else if(b instanceof Array) {
for(var j = 0; j < b.length; j++)
this.a.push(b[j]);
}
}
}

// コピー
this.copy = function() {
return new poly(this.a);
}

// 加算
this.add = function(p) {
var r = this.copy();
if(typeof p == "number")
r.a[0] += p;
else if(p instanceof poly) {
for(var i = 0; i < p.a.length; i++) {
if(i < r.a.length)
r.a[i] += p.a[i];
else
r.a[i] = p.a[i];
}
}
else
throw("unsupported argument in poly::add");

r.normalize();
return r;
}

// 減算
this.subtract = function(p) {
var r = this.copy();
if(typeof p == "number")
r.a[0] -= p;
else if(p instanceof poly) {
for(var i = 0; i < p.a.length; i++) {
if(i < r.a.length)
r.a[i] -= p.a[i];
else
r.a[i] = -p.a[i];
}
}
else
throw("unsupported argument in poly::subtract");

r.normalize();
return r;
}

// 乗算
this.multiply = function(p) {
var r = new poly();
if(typeof p == "number") {
for(var i = 0; i < this.a.length; i++)
r.a[i] *= p;
}
else if(p instanceof poly) {
for(var i = 0; i < this.a.length; i++) {
for(var j = 0; j < p.a.length; j++) {
if(r.a[i+j] == undefined)
r.a[i+j] = this.a[i] * p.a[j];
else
r.a[i+j] += this.a[i] * p.a[j];
}
}
}
else
throw("unsupported argument in poly::multiply");

r.normalize();
return r;
}

// 関数の値
this.value = function(x) {
if(typeof x != "number")
throw("unsupported argument in poly::value");

var r = 0;
for(var i = this.a.length - 1; i >= 0; i--)
r = r * x + this.a[i];

return r;
}

// 合成
this.synthesize = function(p) {
if(!(p instanceof poly))
throw("unsupported argument in poly::synthesize");

var r = new poly();
for(var i = this.a.length - 1; i >= 0; i--)
r = r.multiply(p).add(this.a[i]);

return r;
}

// 積分
this.integral = function() {
var args = this.integral.arguments;
if(args.length == 0) { // 不定積分
var r = new poly();
for(var i = 0; i < this.a.length; i++)
r.a[i+1] = this.a[i] / (i + 1);
return r;
}
else if(args.length == 2) { // 定積分
var r1 = [ ];
var q = this.integral(); // とりあえず不定積分する

for(var i = 0; i < 2; i++) {
var a = args[i];
if(a instanceof poly) // 多項式なら合成
r1[i] = q.synthesize(a);
else
r1[i] = q.value(a);
}

// 種類によって引き算を場合分け
if(r1[1] instanceof poly) {
return r1[1].subtract(r1[0]);
}
else {
if(r1[0] instanceof poly)
return (new poly(r1[1]).subtract(r1[0]));
else
return r1[1] - r1[0];
}
}
else
throw("wrong number of arguments in poly::integral");
}

// 上位の係数0の項は削除する(内部関数)
this.normalize = function() {
// 真の配列の長さを求める
var len = this.a.length;
for( ; len > 1; len--) {
if(this.a[len-1] != 0)
break;
}
this.a.length = len;
}
this.toString = function() {
if(this.a.length == 1) {
return "" + this.a[0];
}
var str = "";
var first = true;
for(var i = 0; i < this.a.length; i++) {
var c = this.a[i];
if(c == 0)
continue;
else if(c > 0) {
if(!first)
str += "+";
if(c != 1 || i == 0)
str += c;
}
else {
if(c == -1 && i > 0)
str += "-";
else
str += c;
}
if(i == 1)
str += "x";
else if(i > 1)
str += "x^" + i;

first = false;
}
return str;
}
}