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; } };