記事「母集団最適化アルゴリズム:SSG(Saplings Sowing and Growing up、苗木の播種と育成)」についてのディスカッション - ページ 12 1...5678910111213 新しいコメント 削除済み 2023.03.28 07:28 #111 Andrey Dik #:そう、とても興味深い生物だ))しかし、ナメクジは2次元しか使わず、最も単純なAOでさえ2次元に対応できる。1000次元でどう振る舞うかは大きな問題で、問題の複雑さは次元数に応じて非線形に増大するからだ。 Antも同じですが、多次元の場合に一般化します。ここでchattgptが質問している。)スライムモールド・アルゴリズムは、スライムモールドの挙動に基づく発見的最適化手法である。このアルゴリズムはChiaro D'Ambrosioによって開発され、「自然な」最適化アルゴリズムのグループに属します。 このアルゴリズムの本質は、特定の地点に「ナメクジ」を置き、最大の餌を求めてその地点の周囲にどのように広がっていくかを観察することである。このアルゴリズムの過程で、ナメクジは痕跡を残し、徐々に蒸発していく。ある地域に残された痕跡が多ければ多いほど、別の「ナメクジ」がその痕跡をより高濃度の餌への道として採用する可能性が高くなる。 最適化の文脈では、このナメクジの行動は、パラメータ空間で最適解を見つけるために使われる。例えば、与えられたパラメータ境界内で関数の最小値を求めるのに、このアルゴリズムを使うことができる。 このアルゴリズムの利点は、単純であることと、多次元空間において最適解に素早く収束できることです。しかし、より複雑で非線形な問題では、最適解への収束に問題が生じることもある。 Andrey Dik 2023.03.28 07:52 #112 Maxim Dmitrievsky #: 蟻のアルゴリズムは同じであるが、多次元の場合に一般化されている。 蟻の収束性は、次元が増加するにつれて低下し、急激に低下する。すべてのアルゴリズムは一般的に同じように振る舞う。) 収束は、次元が増加するにつれて複雑さの増加よりも遅くなる - 私はAOのスケーラビリティを考慮する。スケーラビリティは関数の種類に依存し、異なるアルゴリズムでは異なる関数で異なるスケーリングを行う。 Andrey Dik 2023.03.28 07:55 #113 Maxim Dmitrievsky #: ここでchattgptが尋ねた。) スライムモールド・アルゴリズムは、スライムモールドの行動に基づいた発見的最適化手法です。このアルゴリズムはChiaro D'Ambrosioによって開発され、「自然な」最適化アルゴリズムのグループに属します。 このアルゴリズムの本質は、特定の地点に「ナメクジ」を置き、最大の餌を求めてその地点の周囲にどのように広がっていくかを観察することである。このアルゴリズムの過程で、ナメクジは痕跡を残し、徐々に蒸発していく。ある地域に残された痕跡が多ければ多いほど、別の「ナメクジ」がその痕跡をより高濃度の餌への道として採用する可能性が高くなる。 最適化の文脈では、このナメクジの行動は、パラメータ空間で最適解を見つけるために使われる。例えば、与えられたパラメータ境界内で関数の最小値を求めるのに、このアルゴリズムを使うことができる。 このアルゴリズムの利点は、単純であることと、多次元空間において最適解に素早く収束できることです。しかし、より複雑で非線形な問題では、最適解への収束に問題が生じることもある。 もちろん、分解して組み立てるのも面白い))))))))))))。 Andrey Dik 2023.03.28 08:00 #114 手に入り次第、問題の次元数に対する既知のアルゴリズムすべての収束の比較グラフを3種類の問題で作成するつもりだ。このトリックは非常に時間がかかりそうだ。 削除済み 2023.03.28 08:06 #115 Andrey Dik #:もちろん、分解して組み立てるのも面白い)))))。MQLプログラミング言語でのスラッグ・アルゴリズムの実装例です: ``` double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N) { const double k = 0.1; // トレース蒸発係数 const double stepSize = 0.001; // 「スラグ」移動ステップのサイズ double trail[N]; // 各パーティクルのトレースの配列 double bestTrail[N]; // 最良のトレイルの配列 double bestFitness = DBL_MAX; // ベストフィットネス関数の初期値 double bestX[N]; // 最良の軌跡に対応するパラメータ値の配列 for (int i = 0; i < N; i++) { // 最良の軌跡に対応するパラメータ値の配列 for (int j = 0; j < N; j++) { // 最良の軌跡に対応するパラメータ値の配列 x[j] = LB[j] + (UB[j] - LB[j]) * (double)rand() / RAND_MAX; // パラメータの指定範囲にランダムに「スラッグ」を配置する。 } trail[i] = f(x); // これらのパラメータに対するフィットネス関数の値を計算する。 if (trail[i] < bestFitness) { // もしフィットネス関数の値が現在のベスト値よりも良ければ、それを新しいベスト値として保存する。 bestFitness = trail[i]; memcpy(bestX, x, sizeof(bestX)); // 見つかったパラメータの値をベスト値の配列にコピーする。 memcpy(bestTrail, trail, sizeof(bestTrail)); // 見つかったパラメータの関数値を、最適な関数値の配列にコピーする。 } } for (int iter = 0; iter < maxIter; iter++) { // 反復回数だけループする。 for (int i = 0; i < N; i++) { // 反復回数でループする。 double r[n], d[n]; for (int j = 0; j < n; j++) { { r[j] = (double)n r[j] = (double)rand() / RAND_MAX - 0.5; // ランダムインクリメントベクトル d[j] = r[j] * stepSize * (UB[j] - LB[j]); // ステップ幅の計算 x[j] += d[j]; // パラメータにステップを追加する if (x[j] < LB[j]) { // パラメータが範囲外の場合、境界に残す x[j] = LB[j]; }else if (x[j] > UB[j]) { // パラメータが範囲外であれば、境界線に残す。 x[j] = UB[j]; } } double newTrail = f(x); // 新しいパラメータに対する関数値を計算する。 if (newTrail <= trail[i]) { // 関数値が改善された場合、または変わっていない場合、新しいパラメータと関数値を保存する。 memcpy(bestX, x, sizeof(bestX)); // 新しいパラメータを、最適なパラメータ値の配列にコピーする。 memcpy(bestTrail, &newTrail, sizeof(newTrail)); // 新しい関数値を、最適な関数値の配列にコピーします。 trail[i] = newTrail; // すべての "スラッグ "の関数値の配列に、新しいパラメータの関数値を保存する。 if (bestTrail[i] < bestFitness) { // 現在の最適解が、現在の最適解よりも優れているかどうかをチェックする。 bestFitness = bestTrail[i]; } }else { // 関数の値が悪化した場合、古いパラメータに戻す memcpy(x, bestX, sizeof(bestX)); trail[i] = bestTrail[i]; } } for (int i = 0; i < N; i++) { { トレイル[i] *= (1). trail[i] *= (1.0 - k); // 局所最大値にはまらないようにスラッグトレイルを蒸発させる。 } } return bestFitness; } ``` このコードでは、選択したパラメータのランダムな値を開始点として使用し、各パーティクルを順次処理します。各粒子について、ランダムな増分ベクトルを生成し、ステップサイズを計算し、各パラメータに追加します。その後、新しいパラメータに対する関数値を計算し、値が減少または同じであればそれを保存します。各サイクルの終わりには、局所的な最大値でスタックするのを避けるためにスラグトレースを蒸発させる。おそらくそこを微調整する必要があるでしょう😀 gpt issued Discussion of article "Population 削除済み 2023.03.28 08:09 #116 訂正します:MQLには`memcpy`関数はありません。ある変数から別の変数に値をコピーするには、代入演算子`=` を使用します。以下に、`memcpy`関数を使わずにコードを修正した例を示します: ``` double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N) { const double k = 0.1; // トレース蒸発係数 const double stepSize = 0.001; // 「スラグ」移動ステップのサイズ. double trail[N]; // 各パーティクルのトレースの配列 double bestTrail[N]; // 最良のトレイルの配列 double bestFitness = DBL_MAX; // ベストフィットネス関数の初期値 double bestX[N]; // 最良の軌跡に対応するパラメータ値の配列 for (int i = 0; i < N; i++) { // 最良の軌跡に対応するパラメータ値の配列 for (int j = 0; j < N; j++) { // 最良の軌跡に対応するパラメータ値の配列 x[j] = LB[j] + (UB[j] - LB[j]) * (double)rand() / RAND_MAX; // パラメータの指定範囲にランダムに「スラッグ」を配置する。 } trail[i] = f(x); // これらのパラメータに対するフィットネス関数の値を計算する。 if (trail[i] < bestFitness) { // もしフィットネス関数の値が現在のベスト値よりも良ければ、それを新しいベスト値として保存する。 bestFitness = trail[i]; for (int j = 0; j < n; j++) { { { for (int j = 0; j < n; j++) bestX[j] = x[j]; // 見つかったパラメータの値をベスト値の配列にコピーする。 } bestTrail[i] = trail[i]; // 見つかったパラメータの関数値を最適関数値の配列にコピーする。 } } for (int iter = 0; iter < maxIter; iter++) { // 反復回数をループする。 for (int i = 0; i < N; i++) { // 反復回数をループする。 double r[n], d[n]; for (int j = 0; j < n; j++) { // 繰り返し回数をループする。 r[j] = (double)rand() / RAND_MAX - 0.5; // ランダムインクリメントベクトル d[j] = r[j] * stepSize * (UB[j] - LB[j]); // ステップ幅を計算する。 x[j] += d[j]; // パラメータにステップを追加する if (x[j] < LB[j]) { // パラメータが範囲外の場合、境界に残す x[j] = LB[j]; }else if (x[j] > UB[j]) { // パラメータが範囲外であれば、境界線に残す。 x[j] = UB[j]; } } double newTrail = f(x); // 新しいパラメータに対する関数値を計算する。 if (newTrail <= trail[i]) { // 関数値が改善された場合、または変わっていない場合、新しいパラメータと関数値を保存する。 for (int j = 0; j < n; j++) { // 新しいパラメータと関数値を保存する。 bestX[j] = x[j]; // 新しいパラメータを最適なパラメータ値の配列にコピーする。 } bestTrail[i] = newTrail; // 新しい関数値を最適な関数値の配列にコピーする。 trail[i] = newTrail; // すべての "スラッグ "の関数値の配列に、新しいパラメータの関数値を格納する。 if (bestTrail[i] < bestFitness) { // 現在の最適解が、現在の最適解よりも優れているかどうかをチェックする。 bestFitness = bestTrail[i]; } }else { // 関数の値が悪化した場合、古いパラメータに戻す for (int j = 0; j < n; j++) { // 関数値が悪化した場合、古いパラメータに戻す。 x[j] = bestX[j]; } trail[i] = bestTrail[i]; } } for (int i = 0; i < N; i++) { 以下のようにする。 trail[i] *= (1.0 - k); // 局所最大値にはまらないようにスラッグトレイルを蒸発させる。 } } return bestFitness; } ``` 見ての通り、ループを使って配列の値を要素ごとにコピーしているだけだ。 Discussion of article "Population Andrey Dik 2023.03.28 08:10 #117 このハードウェアはすべてを知っている...))) 一般的なケースでは、おそらく、ハードウェアがそのタスクに適したアルゴを選択することを信頼することができますが、私は自分の評価表を見る方が好きです))) Andrey Dik 2023.03.28 08:14 #118 Maxim Dmitrievsky #: ``` double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N) 記事中のすべてのアルゴリズムがそうであるように、これをスキームに分解する必要があるだろう。 削除済み 2023.03.28 08:17 #119 Andrey Dik #:このままでは役に立たない。 チャットで試してみてください、そのようなタスクを処理できるかどうか疑問です :) 私はトークンが不足しているので、時々手を出す。彼に記事の例を与え、スタイルを維持する。ところで、1ペニーのための偉大なスタートアップは、カートにそのようなボットです。 Andrey Dik 2023.03.28 09:03 #120 Maxim Dmitrievsky #: チャットで試してみてください、そのようなタスクに対応してくれるでしょうか :) トークンが足りなくなったので、時々手を出しています。サンプル記事を与えて、スタイルに従わせる。ちなみに、1ペニーで買えるスタートアップには、カートに入ったこんなボットがある。 残念なことに、あるいは幸いなことに、このボットは新しい情報を生み出さないし、公開されている情報はしばしば歪曲されている。これは情報補間のメカニズムによるものだと思う。嘘をついたり、存在しないアルゴリズムの略称をでっち上げたり、その場で著者の名前やアルゴリズムが登場した日付まででっち上げたりする)))。このような情報には十分注意すべきだ。 文章編集のアシスタントとして、文体修正、記事執筆時の参考資料として、そう、なくてはならないアシスタントなのだ。 1...5678910111213 新しいコメント 取引の機会を逃しています。 無料取引アプリ 8千を超えるシグナルをコピー 金融ニュースで金融マーケットを探索 新規登録 ログイン スペースを含まないラテン文字 このメールにパスワードが送信されます エラーが発生しました Googleでログイン WebサイトポリシーおよびMQL5.COM利用規約に同意します。 新規登録 MQL5.com WebサイトへのログインにCookieの使用を許可します。 ログインするには、ブラウザで必要な設定を有効にしてください。 ログイン/パスワードをお忘れですか? Googleでログイン
そう、とても興味深い生物だ))
しかし、ナメクジは2次元しか使わず、最も単純なAOでさえ2次元に対応できる。1000次元でどう振る舞うかは大きな問題で、問題の複雑さは次元数に応じて非線形に増大するからだ。
スライムモールド・アルゴリズムは、スライムモールドの挙動に基づく発見的最適化手法である。このアルゴリズムはChiaro D'Ambrosioによって開発され、「自然な」最適化アルゴリズムのグループに属します。
このアルゴリズムの本質は、特定の地点に「ナメクジ」を置き、最大の餌を求めてその地点の周囲にどのように広がっていくかを観察することである。このアルゴリズムの過程で、ナメクジは痕跡を残し、徐々に蒸発していく。ある地域に残された痕跡が多ければ多いほど、別の「ナメクジ」がその痕跡をより高濃度の餌への道として採用する可能性が高くなる。
最適化の文脈では、このナメクジの行動は、パラメータ空間で最適解を見つけるために使われる。例えば、与えられたパラメータ境界内で関数の最小値を求めるのに、このアルゴリズムを使うことができる。
このアルゴリズムの利点は、単純であることと、多次元空間において最適解に素早く収束できることです。しかし、より複雑で非線形な問題では、最適解への収束に問題が生じることもある。
蟻のアルゴリズムは同じであるが、多次元の場合に一般化されている。
蟻の収束性は、次元が増加するにつれて低下し、急激に低下する。すべてのアルゴリズムは一般的に同じように振る舞う。)
収束は、次元が増加するにつれて複雑さの増加よりも遅くなる - 私はAOのスケーラビリティを考慮する。スケーラビリティは関数の種類に依存し、異なるアルゴリズムでは異なる関数で異なるスケーリングを行う。
スライムモールド・アルゴリズムは、スライムモールドの行動に基づいた発見的最適化手法です。このアルゴリズムはChiaro D'Ambrosioによって開発され、「自然な」最適化アルゴリズムのグループに属します。
このアルゴリズムの本質は、特定の地点に「ナメクジ」を置き、最大の餌を求めてその地点の周囲にどのように広がっていくかを観察することである。このアルゴリズムの過程で、ナメクジは痕跡を残し、徐々に蒸発していく。ある地域に残された痕跡が多ければ多いほど、別の「ナメクジ」がその痕跡をより高濃度の餌への道として採用する可能性が高くなる。
最適化の文脈では、このナメクジの行動は、パラメータ空間で最適解を見つけるために使われる。例えば、与えられたパラメータ境界内で関数の最小値を求めるのに、このアルゴリズムを使うことができる。
このアルゴリズムの利点は、単純であることと、多次元空間において最適解に素早く収束できることです。しかし、より複雑で非線形な問題では、最適解への収束に問題が生じることもある。
もちろん、分解して組み立てるのも面白い))))))))))))。
もちろん、分解して組み立てるのも面白い)))))。
MQLプログラミング言語でのスラッグ・アルゴリズムの実装例です:
```
double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N)
{
const double k = 0.1; // トレース蒸発係数
const double stepSize = 0.001; // 「スラグ」移動ステップのサイズ
double trail[N]; // 各パーティクルのトレースの配列
double bestTrail[N]; // 最良のトレイルの配列
double bestFitness = DBL_MAX; // ベストフィットネス関数の初期値
double bestX[N]; // 最良の軌跡に対応するパラメータ値の配列
for (int i = 0; i < N; i++) { // 最良の軌跡に対応するパラメータ値の配列
for (int j = 0; j < N; j++) { // 最良の軌跡に対応するパラメータ値の配列
x[j] = LB[j] + (UB[j] - LB[j]) * (double)rand() / RAND_MAX; // パラメータの指定範囲にランダムに「スラッグ」を配置する。
}
trail[i] = f(x); // これらのパラメータに対するフィットネス関数の値を計算する。
if (trail[i] < bestFitness) { // もしフィットネス関数の値が現在のベスト値よりも良ければ、それを新しいベスト値として保存する。
bestFitness = trail[i];
memcpy(bestX, x, sizeof(bestX)); // 見つかったパラメータの値をベスト値の配列にコピーする。
memcpy(bestTrail, trail, sizeof(bestTrail)); // 見つかったパラメータの関数値を、最適な関数値の配列にコピーする。
}
}
for (int iter = 0; iter < maxIter; iter++) { // 反復回数だけループする。
for (int i = 0; i < N; i++) { // 反復回数でループする。
double r[n], d[n];
for (int j = 0; j < n; j++) { { r[j] = (double)n
r[j] = (double)rand() / RAND_MAX - 0.5; // ランダムインクリメントベクトル
d[j] = r[j] * stepSize * (UB[j] - LB[j]); // ステップ幅の計算
x[j] += d[j]; // パラメータにステップを追加する
if (x[j] < LB[j]) { // パラメータが範囲外の場合、境界に残す
x[j] = LB[j];
}else if (x[j] > UB[j]) { // パラメータが範囲外であれば、境界線に残す。
x[j] = UB[j];
}
}
double newTrail = f(x); // 新しいパラメータに対する関数値を計算する。
if (newTrail <= trail[i]) { // 関数値が改善された場合、または変わっていない場合、新しいパラメータと関数値を保存する。
memcpy(bestX, x, sizeof(bestX)); // 新しいパラメータを、最適なパラメータ値の配列にコピーする。
memcpy(bestTrail, &newTrail, sizeof(newTrail)); // 新しい関数値を、最適な関数値の配列にコピーします。
trail[i] = newTrail; // すべての "スラッグ "の関数値の配列に、新しいパラメータの関数値を保存する。
if (bestTrail[i] < bestFitness) { // 現在の最適解が、現在の最適解よりも優れているかどうかをチェックする。
bestFitness = bestTrail[i];
}
}else { // 関数の値が悪化した場合、古いパラメータに戻す
memcpy(x, bestX, sizeof(bestX));
trail[i] = bestTrail[i];
}
}
for (int i = 0; i < N; i++) { { トレイル[i] *= (1).
trail[i] *= (1.0 - k); // 局所最大値にはまらないようにスラッグトレイルを蒸発させる。
}
}
return bestFitness;
}
```
このコードでは、選択したパラメータのランダムな値を開始点として使用し、各パーティクルを順次処理します。各粒子について、ランダムな増分ベクトルを生成し、ステップサイズを計算し、各パラメータに追加します。その後、新しいパラメータに対する関数値を計算し、値が減少または同じであればそれを保存します。各サイクルの終わりには、局所的な最大値でスタックするのを避けるためにスラグトレースを蒸発させる。
おそらくそこを微調整する必要があるでしょう😀 gpt issued
MQLには`memcpy`関数はありません。ある変数から別の変数に値をコピーするには、代入演算子`=` を使用します。以下に、`memcpy`関数を使わずにコードを修正した例を示します:
```
double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N)
{
const double k = 0.1; // トレース蒸発係数
const double stepSize = 0.001; // 「スラグ」移動ステップのサイズ.
double trail[N]; // 各パーティクルのトレースの配列
double bestTrail[N]; // 最良のトレイルの配列
double bestFitness = DBL_MAX; // ベストフィットネス関数の初期値
double bestX[N]; // 最良の軌跡に対応するパラメータ値の配列
for (int i = 0; i < N; i++) { // 最良の軌跡に対応するパラメータ値の配列
for (int j = 0; j < N; j++) { // 最良の軌跡に対応するパラメータ値の配列
x[j] = LB[j] + (UB[j] - LB[j]) * (double)rand() / RAND_MAX; // パラメータの指定範囲にランダムに「スラッグ」を配置する。
}
trail[i] = f(x); // これらのパラメータに対するフィットネス関数の値を計算する。
if (trail[i] < bestFitness) { // もしフィットネス関数の値が現在のベスト値よりも良ければ、それを新しいベスト値として保存する。
bestFitness = trail[i];
for (int j = 0; j < n; j++) { { { for (int j = 0; j < n; j++)
bestX[j] = x[j]; // 見つかったパラメータの値をベスト値の配列にコピーする。
}
bestTrail[i] = trail[i]; // 見つかったパラメータの関数値を最適関数値の配列にコピーする。
}
}
for (int iter = 0; iter < maxIter; iter++) { // 反復回数をループする。
for (int i = 0; i < N; i++) { // 反復回数をループする。
double r[n], d[n];
for (int j = 0; j < n; j++) { // 繰り返し回数をループする。
r[j] = (double)rand() / RAND_MAX - 0.5; // ランダムインクリメントベクトル
d[j] = r[j] * stepSize * (UB[j] - LB[j]); // ステップ幅を計算する。
x[j] += d[j]; // パラメータにステップを追加する
if (x[j] < LB[j]) { // パラメータが範囲外の場合、境界に残す
x[j] = LB[j];
}else if (x[j] > UB[j]) { // パラメータが範囲外であれば、境界線に残す。
x[j] = UB[j];
}
}
double newTrail = f(x); // 新しいパラメータに対する関数値を計算する。
if (newTrail <= trail[i]) { // 関数値が改善された場合、または変わっていない場合、新しいパラメータと関数値を保存する。
for (int j = 0; j < n; j++) { // 新しいパラメータと関数値を保存する。
bestX[j] = x[j]; // 新しいパラメータを最適なパラメータ値の配列にコピーする。
}
bestTrail[i] = newTrail; // 新しい関数値を最適な関数値の配列にコピーする。
trail[i] = newTrail; // すべての "スラッグ "の関数値の配列に、新しいパラメータの関数値を格納する。
if (bestTrail[i] < bestFitness) { // 現在の最適解が、現在の最適解よりも優れているかどうかをチェックする。
bestFitness = bestTrail[i];
}
}else { // 関数の値が悪化した場合、古いパラメータに戻す
for (int j = 0; j < n; j++) { // 関数値が悪化した場合、古いパラメータに戻す。
x[j] = bestX[j];
}
trail[i] = bestTrail[i];
}
}
for (int i = 0; i < N; i++) { 以下のようにする。
trail[i] *= (1.0 - k); // 局所最大値にはまらないようにスラッグトレイルを蒸発させる。
}
}
return bestFitness;
}
```
見ての通り、ループを使って配列の値を要素ごとにコピーしているだけだ。
このハードウェアはすべてを知っている...)))
一般的なケースでは、おそらく、ハードウェアがそのタスクに適したアルゴを選択することを信頼することができますが、私は自分の評価表を見る方が好きです)))
```
double slimeMouldAlgorithm(double(*f)(double[]), double x[], int n, double LB[], double UB[], int maxIter, int N)
記事中のすべてのアルゴリズムがそうであるように、これをスキームに分解する必要があるだろう。
このままでは役に立たない。
チャットで試してみてください、そのようなタスクに対応してくれるでしょうか :) トークンが足りなくなったので、時々手を出しています。サンプル記事を与えて、スタイルに従わせる。ちなみに、1ペニーで買えるスタートアップには、カートに入ったこんなボットがある。
残念なことに、あるいは幸いなことに、このボットは新しい情報を生み出さないし、公開されている情報はしばしば歪曲されている。これは情報補間のメカニズムによるものだと思う。嘘をついたり、存在しないアルゴリズムの略称をでっち上げたり、その場で著者の名前やアルゴリズムが登場した日付まででっち上げたりする)))。このような情報には十分注意すべきだ。
文章編集のアシスタントとして、文体修正、記事執筆時の参考資料として、そう、なくてはならないアシスタントなのだ。