webAi.JpMrkv - ソースコード
説明
「Web AI」は、クロノス・クラウンの柳井政和が開発しているJavaScirptのライブラリです。「Web AI」を使うと、Web上からデータを取得して構築するWebページを、とても簡単に構築することができます。
「Web AI」は、Webからの検索結果やフィードの取得、短縮URLへのURLの変換、日本語文章からのキーワードや文章抽出、日本語文章のマルコフ連鎖、クエリーの管理などの機能を備えています。
このページでは、この「Web AI」のファイルの1つである「crocro.webAi.JpMrkv.js」のソースコードを掲載します。
「crocro.webAi.JpMrkv.js」
「crocro.webAi.JpMrkv.js」には、日本語の文章から、マルコフ連鎖を使って、シャッフルした新しい文章を作成する機能がまとめられています。このファイルには「crocro.webAi.JpMrkv」オブジェクトが収録されています。
使い方は、「Web AI」の各サンプルを参考にしてください。
ソースコード
以下、「crocro.webAi.JpMrkv.js」のソースコードです。
// crocro.webAiの初期化
if (! crocro) var crocro = {};
if (! crocro.webAi) crocro.webAi = {};
/**
* @variable crocro.webAi.JpMrkv(arg)
* @title 日本語マルコフ連鎖
* @description
*
* 日本語の文章から、マルコフ連鎖を行うオブジェクト。
* newで初期化してから利用する。
*
* 引数は連想配列で設定する。例){~: "~", …}
*
* @param arg.ndMax 文章の最大ノード。
* @param arg.chain 接頭文字数(デフォルトは1。2以上の場合は、似たような文章が大量に必要)。
* @param arg.bsSntncLen 基本文章長。
*/
crocro.webAi.JpMrkv = function(arg) {
// ユーザー変数
/**
* @variable JpMrkv.ndMax
* @title 文章の最大ノード
* @description
* 生成する文章の長さを規定する。長さの単位はノード。何ノードまでの連結を許すか。
*
* デフォルトは「300」。
*/
/**
* @variable JpMrkv.chain
* @title 接頭文字数
* @description
* ノード連結単位。1の場合は、前後の繋がりだけを見る。
* 2以上の場合は、2つ前、3つ前の繋がりも見て確認する。
*
* デフォルトは「1」。「2」以上の場合は、似たような文章が大量に必要になる。
*/
/**
* @variable JpMrkv.bsSntncLen
* @title 基本文章長
* @description
* だいたい、この文字数になるように連結を目指す値。
*
* デフォルトは「25」。
*/
this.ndMax = 300; // 文章の最大ノード
this.chain = 1; // 接頭文字数(デフォルトは1。2以上の場合は、似たような文章が大量に必要)
this.bsSntncLen = 25; // 基本文章長
// ユーザー変数の設定
if (arg) {
if ("ndMax" in arg) this.ndMax = arg.ndMax;
if ("chain" in arg) this.chain = arg.chain;
if ("bsSntncLen" in arg) this.bsSntncLen = arg.bsSntncLen;
}
// 内部利用変数
this.dic = {}; // 辞書
this.dbgStr = ""; // デバッグ用文字列
// 日本語分割用正規表現
/**
* @variable JpMrkv.bsRe
* @title 日本語分割用正規表現
* @description
* 日本語の文章をノードに分解するための正規表現。
* この値を差し替えると、違う分割でマルコフ連鎖を行える。
*/
this.bsRe = new RegExp(
""
+ "「.+?」|『.+?』|(.+?)|\(.+?\)|"
+ "[ァ-ヶ・=ー0-90-9一-龠A-Za-z0-9\w\-]{2,}|"
+ "[一-龠々〆ヵヶ]+、{0,1}|[ぁ-ん]+、{0,1}|[ァ-ヴー]+、{0,1}|"
+ "[a-zA-Z0-9]+、{0,1}|[a-zA-Z0-9]+、{0,1}|"
+ "[、。!!??()()「」『』]{1}",
"g"
);
// キー内の位置
KEY_STR = 0; // 文字列
KEY_POS = 1; // 位置
KEY_LEN = 2; // 全体サイズ
/*
*--------------------------------------------------
*/
/**
* @access private
* @variable JpMrkv.Rndm(dbgSeed_)
* @title ランダム
* @description
*
* ランダムな値を取得するためのオブジェクト。
* デバッグ・シードでのランダムは、デバッグ用途なので精度は悪い。
*
* @param dbgSeed_ デバッグ・シードを利用するか。
* 指定なしか-1の時は通常のランダム。
* @return ランダムな値。
*/
function Rndm(dbgSeed_) {
var dbgSeed = dbgSeed_ || -1;
var nowKey = 2*3*5*7*11*13*17-1 - dbgSeed;
/**
* @access private
* @variable JpMrkv.Rndm.nextInt(strt, max)
* @title 数値取得
* @description
*
* ランダムな値を取得する。
*
* @param strt 開始数値。
* @param strt 最大数値(この値未満となる)。
* @return ランダムな値。
*/
this.nextInt = function(strt, max) {
var res = 0;
var rng = max - strt;
if (dbgSeed == -1) {
res = strt + Math.floor(Math.random() * rng); // 通常
} else {
nowKey = (nowKey * 1103515245 + 12345);
if (nowKey < 0) {
nowKey /= 2;
nowKey *= -1;
}
nowKey = nowKey & 2147483647;
res = strt + nowKey % rng; // デバッグ・シード
}
return res;
}
}
/*
*--------------------------------------------------
*/
/**
* @variable JpMrkv.reset(arg)
* @title リセット
* @description
*
* 内容をリセットしてメモリを解放する。
* 設定はそのまま維持する。
*
* @return なし。
*/
this.reset = function() {
this.dic = {}; // 辞書
this.dbgStr = ""; // デバッグ用文字列
}
/**
* @variable JpMrkv.setSntncArr(srcArr)
* @title 文章の登録
* @description
*
* 文章の配列を引数として、マルコフ連鎖用の文章を登録する。
*
* @param srcArr 元になる文章配列を指定。
* @return なし。
*/
this.setSntncArr = function(srcArr) {
for (var i = 0; i < srcArr.length; i ++) {
this.addSntnc(srcArr[i]);
}
}
/**
* @variable JpMrkv.addSntnc(srcStr)
* @title 文章の登録
* @description
*
* 通常は内部で使用する、文章追加メソッド。
*
* @param srcStrr 元になる文章を指定。
* @return なし。
*/
this.addSntnc = function(srcStr) {
var strArr = srcStr.match(this.bsRe);
if (! strArr) return;
// 追加可能文章か判定
if (! srcStr.match("[るただい]$")) return;
// デバッグ用
//this.dbgStr += srcStr + "<BR>[" + strArr + "]<BR><BR>";
strArr.unshift("_START_"); // 先頭
// n接頭文字1接尾文字
var n = this.chain; // 接頭文字の数
// 末尾の追加
for (var i = 0; i < n; i ++) {strArr.push("_END_");}
for (var i = 0; i < strArr.length; i ++) {
if (strArr[i] == "0") {strArr[i] = "0";} // ハッシュに使えない添え字対策
}
var strArrLen = strArr.length;
var strArrPos = 0;
while (strArr[n]) { // 文字列配列が残っている間実行
if (! this.dic[strArr[0]]) this.dic[strArr[0]] = new Array();
var pntr = this.dic[strArr[0]]; // 配列のポインタ
if (n > 1) {
for (var i = 1; i <= n; i ++) {
// キー配列の追加
if (! pntr["_KEY_"]) pntr["_KEY_"] = []; // キー配列の作製
var keyArr = new Array();
keyArr[KEY_STR] = strArr[i]; // 文字列
keyArr[KEY_POS] = strArrPos; // 位置
keyArr[KEY_LEN] = strArrLen; // 全体サイズ
pntr["_KEY_"].push(keyArr);
strArrPos ++;
// ツリーの追加
if (! pntr[strArr[i]]) pntr[strArr[i]] = []; // ツリーの作製
pntr[strArr[i]].push(strArr[i + 1]);
pntr = pntr[strArr[i]];
}
} else {
pntr.push(strArr[1]);
}
strArr.shift(); // 先頭を削除
}
};
/*
*--------------------------------------------------
*/
/**
* @variable JpMrkv.getDumpDic()
* @title 辞書のダンプ
* @description
*
* 辞書の構造を「<ul><li>~</li></ul>」のHTML文字列として取得する。
*
* @return 辞書の構造をツリーにしたHTML文字列。
*/
this.getDumpDic = function() {
var resStr = getDump(this.dic);
return resStr;
}
/**
* @access private
* @variable JpMrkv.getDump(obj)
* @title 辞書のダンプ(内部再帰)
* @description
* 辞書のダンプで使用する再帰関数。
* @param obj ノードのオブジェクト。
* @return 辞書の構造をツリーにしたHTML文字列。
*/
function getDump(obj) {
var resStr = '<UL style="margin-left: 1em;">';
for (var prop in obj){
resStr += "<LI>" + prop + "=" + obj[prop] + "</LI>";
resStr += this.getDump(obj[prop]);
}
resStr += "</UL>";
return resStr;
}
/*
*--------------------------------------------------
*/
/**
* @variable JpMrkv.getSntnc(arg)
* @title マルコフ連鎖の実行
* @description
*
* ランダム・キーと、開始に使用したい文字列で、マルコフ連鎖を実行する。
*
* @param arg.rndKey ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
* @param arg.strtStr 開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
* @return 作成した文章。
*/
this.getSntnc = function(arg) {
// 変数の初期化
var rndKey = -1;
var strtStr_ = "";
if (arg) {
if ("rndKey" in arg) rndKey = arg.rndKey;
if ("strtStr" in arg) strtStr_ = arg.strtStr;
}
// 変数の初期化
var sntnc = '';
var sntCnt = 0; // センテンスの長さ
var rndm = new Rndm(rndKey); // シード付きランダム
var strtStr = '_START_';
if (strtStr_ != "" && this.dic[strtStr]) {
strtStr = strtStr_;
sntnc += strtStr_;
}
// n接頭文字1接尾文字
if (this.dic[strtStr]) {
// 接頭文字の数
var n = this.chain;
var w = new Array();
var pntr;
// 最初の連鎖の初期化
pntr = this.dic[strtStr];
if (pntr["_KEY_"]) {
w[0] = getRndFrmKeyPntr(pntr, rndm, 0);
} else {
w[0] = getRndFrmPntr(pntr, rndm);
}
sntnc += w[0];
sntCnt ++;
for (var j = 1; j < n; j ++) {
pntr = pntr[w[j - 1]];
if (pntr["_KEY_"]) {
w[j] = getRndFrmKeyPntr(pntr, rndm, j);
} else {
w[j] = getRndFrmPntr(pntr, rndm);
}
}
// 連鎖で結合
for (var i = 0; i <= this.ndMax; i ++) {
// 文章の連結
if (w[1] == "_END_") break; // 末尾なので終了
if (w[1]) {
sntnc += w[1]; // 1接頭文字対策
sntCnt ++;
}
pntr = this.dic[w[0]];
if (n > 1) {
var j = 1;
for (; j < n; j ++) {
pntr = pntr[w[j]];
}
if (pntr["_KEY_"]) {
w[j] = getRndFrmKeyPntr(pntr, rndm, sntCnt + j);
} else {
w[j] = getRndFrmPntr(pntr, rndm);
}
} else {
w[1] = getRndFrmPntr(pntr, rndm);
}
// 次の位置にスライド
for (var j = 0; j < n; j ++) {
w[j] = w[j + 1];
}
}
}
// 短すぎるセンテンスは無効
if (sntCnt <= 2) return "";
// 戻り値を戻して終了
return sntnc;
};
/**
* @access private
* @variable getRndFrmPntr(pntr, rndm)
* @title ポインタからランダム取得
* @description
*
* ポインタから、連鎖のツリーをランダムで取得。
*
* @param pntr ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
* @param rndm 開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
* @return 文字列。
*/
function getRndFrmPntr(pntr, rndm) {
var w = pntr[rndm.nextInt(0, pntr.length)];
return w;
}
/**
* @access private
* @variable JpMrkv.getRndFrmKeyPntr(pntr, rndm, sntPos)
* @title キー・ポインタからランダム取得
* @description
*
* ポインタから、連鎖のツリーをランダムで取得。
* 文章中の、どの辺りにある単語かで、優先順位を変える。
*
* @param pntr ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
* @param rndm 開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
* @param sntPos 文章内での位置。
* @return 文字列。
*/
function getRndFrmKeyPntr(pntr, rndm, sntPos) {
var keyArr = pntr["_KEY_"];
var newArr = new Array();
for (var i = 0; i < keyArr.length; i ++) {
// 文章中の、どの辺りにある単語かで、優先順位を変える。
var bsPer = Math.min(sntPos / this.bsSntncLen * 100, 100);
var dtPer = Math.min(keyArr[i][KEY_POS] / keyArr[i][KEY_LEN] * 100, 100);
var dst = (dtPer - bsPer) / 10;
if (dst < 0) dst = dst * -1;
var len = Math.max(1, 10 - dst); // 大きい方を取得
for (var j = 0; j < len; j ++) {
newArr.push(keyArr[i][KEY_STR]);
}
}
var w = newArr[rndm.nextInt(0, newArr.length)];
return w;
}
};







