Go to the first, previous, next, last section, table of contents.


リスト(並び)としてのベクトル(Vectors as Lists)

Calc はベクトルや行列を数学的オブジェクトとして扱うための 多くの機能を備えていますが、 ベクトルを単に値のリスト(並び)として扱うこともできます。 例えば以前に、k f コマンドが数値の素因数のリストを返すことを学びました。

リスト演算コマンド群

スタック項をリスト(ベクトル)にまとめたり、バラしたりできます。

3:  10         1:  [10, 20, 30]     3:  10
2:  20             .                2:  20
1:  30                              1:  30
    .                                   .

                   M-3 v p              v u

連続する整数のリスト(ベクトル)や、 与えられた数のコピーでできたリスト(ベクトル)を作ることができます。

1:  [1, 2, 3, 4]    2:  [1, 2, 3, 4]    2:  [1, 2, 3, 4]
    .               1:  17              1:  [17, 17, 17, 17]
                        .                   .

    v x 4 RET           17                  v b 4 RET

マッピング(mapping) コマンドを使うと、 リスト(ベクトル)のどの要素にも演算子を作用させることができます。

1:  [17, 34, 51, 68]   1:  [289, 1156, 2601, 4624]  1:  [17, 34, 51, 68]
    .                      .                            .

    V M *                  2 V M ^                      V M Q

最初の例では、 整数を並べたリスト(ベクトル)に 17のコピーからなるリスト(ベクトル)を要素ごとに掛けました。 2番目では、各要素を 2乗しました。 (基本的に両オペランドは、同じ次元のリスト(ベクトル)でなければならないか、 さもなければ片方がリスト(ベクトル)で片方が数値でなければなりません) 最後の例では、各要素のルートを取りました。

(*) 練習問題 1. 2のべき乗でできたリスト(ベクトル)を 2^-4 から 2^4 まで計算しなさい。リスト 練習問題 1 解答「2 のべき乗」 参照 . (*)

リスト(ベクトル)の要素全部に2項演算子をまとめ作用(reduction)させることもできます。 例えば、`*' のまとめ作用はリスト(ベクトル)の全要素の積を計算します。

1:  123123     1:  [3, 7, 11, 13, 41]      1:  123123
    .              .                           .

    123123         k f                         V R *

この例では、123123 を素因数分解し、それから全因数を掛け合わせて元の数値を 得ました。

マッピングとまとめ作用を使うと、内積を「手動で」計算することができます。

2:  [1, 2, 3]     1:  [7, 12, 0]     1:  19
1:  [7, 6, 0]         .                  .
    .

    r 1 r 2           V M *              V R +

前節から 2つのリスト(ベクトル)をリコールして、要素ごとの積の合計を求め、 前の内積と同じ答を得ました。

まとめ作用のちょっとした応用が蓄積演算 V U です。 これはまとめ作用の中間結果を並べたリスト(ベクトル)を生成します。 これを使って階乗の表を作ってみましょう。

1:  [1, 2, 3, 4, 5, 6]    1:  [1, 2, 6, 24, 120, 720]
    .                         .

    v x 6 RET                 V U *

リストの省略表示

Calc はいくらでも次元の大きなリスト(ベクトル)を許容しますが、 要素数がおよそ 100 を越えると若干遅くなります。 実は、ほとんどの時間は大きなリスト(ベクトル)を表示するための整形に費やされていて、 計算時間ではありません。 次の実験をやってみましょう(もしあなたのコンピューターが高速なら、 この例よりも大きなリスト(ベクトル)でやってみる必要があるかもしれません)。

1:  [1, 2, 3, 4, ...      1:  [2, 3, 4, 5, ...
    .                         .

    v x 500 RET               1 V M +

ここで、v . (文字 v, そしてピリオド)を押して、 もう一度先の実験を行ってみてください。 v . モードでは、長いリスト(ベクトル)は次のように「省略して」表示されます。

1:  [1, 2, 3, ..., 500]   1:  [2, 3, 4, ..., 501]
    .                         .

    v x 500 RET               1 V M +

(ここで、今や `...' は Calc の実際の表示の一部です。) あなたはかなり速くなったと感じたことでしょう。しかし、 たとえ v . モードでもトレイルの中では 全部表示されていることに注意してください。 t . を押すとトレイルも同じく省略表示になりますから、 それでもう一度実験してみましょう。 長いリスト(ベクトル)の演算が、今やとても速くなりました! (しかし t . を使った場合は、 t y コマンドで古いリスト(ベクトル)を取ってくることは当然できません。)

v . モードがアクティブのときにリスト(ベクトル)全体を見る簡単な方法は、 ` (バッククォート)でリスト(ベクトル)を編集することです。 編集機能は常に、省略のない全体表示で動作します。

最小二乗法

ちょっと大規模な例として、最小二乗法による直線回帰をやってみましょう。 (Calc は最小二乗法による近似曲線の組込みコマンドを持っていますが、 リスト(ベクトル)操作の練習として手動でやります。) Emacs に読みこんだファイルに次の数値リストがあったと想定してください。

  x        y
 ---      ---
 1.34    0.234
 1.41    0.298
 1.49    0.402
 1.56    0.412
 1.64    0.466
 1.73    0.473
 1.82    0.601
 1.91    0.519
 2.01    0.603
 2.11    0.637
 2.22    0.645
 2.33    0.705
 2.45    0.917
 2.58    1.009
 2.71    0.971
 2.85    1.062
 3.00    1.148
 3.15    1.157
 3.32    1.354

もしあなたがこのチュートリアルを印刷物で読んでいるなら、 M-# i を押してオンラインの Info 版マニュアルを起動し、 この表を見つけるのがいちばん簡単です。 (g を押し、次に List Tutorial とタイプすると 直接この節に飛び込めます。)

この表の左上、1.34 のすぐ左にカーソルを置いてください。 C-@ を押してマークをセットします。(あなたのシステムによっては C-2, C-SPC, NUL かもしれません。) 次にカーソルを表の右下、1.354 のすぐ後に持ってきてください。 これでリージョンを Emacs の "rectangle" として定義したことになります。 Info バッファに留まったまま、M-# r をタイプしてください。 このコマンド(calc-grab-rectangle)によって、 カーソルはCalc バッファに戻り、あなたが定義した rectangle の内容は 行列形式で入力されます。

1:  [ [ 1.34, 0.234 ]
      [ 1.41, 0.298 ]
      ...

あなたは v . モードを使って、この大きな行列を省略表示してもかまいません。

この行列を一対のリストとして扱いたいので、まず転置して 2行にします。 行列はベクトルのベクトルに過ぎないことを思い出してください。 だからこの行列は一対の行ベクトルに分解できます。

1:  [ [ 1.34,  1.41,  1.49,  ... ]     2:  [1.34, 1.41, 1.49, ... ]
      [ 0.234, 0.298, 0.402, ... ] ]   1:  [0.234, 0.298, 0.402, ... ]
    .                                      .

    v t                                    v u

これらはそれぞれクイック変数 1, 2 にストアしておきましょう。

1:  [1.34, 1.41, 1.49, ... ]        .
    .

    t 2                             t 1

繰り返しますが、t 2s 2 の変形で、 値をストアした後スタックから取り除きます。

最小二乗法による近似では、傾き m は次の式で与えられます。

m = (N sum(x y) - sum(x) sum(y)) / (N sum(x^2) - sum(x)^2)

ここで、 sum(x) は全ての x の合計を表します。 Calc には現実に sum 関数がありますが、 単にまとめ作用を使ってリスト(ベクトル)の合計を得ることはより簡単です。 まず、上式で使う 4種類の合計を求めましょう。

1:  41.63                 1:  98.0003
    .                         .

 r 1 V R +   t 3           r 1 2 V M ^ V R +   t 4

1:  13.613                1:  33.36554
    .                         .

 r 2 V R +   t 5           r 1 r 2 V M * V R +   t 6

これらはそれぞれ `sum(x)', `sum(x^2)', `sum(y)', `sum(x y)' です。 (`sum(x^2)'`sum(x y)' を算出するのに、 内積コマンド * を使う方法もありました。)

最後に、データ点の数 N も必要です。 これは両リストの長さに相当します。

1:  19
    .

 r 1 v l   t 7

(上で使っているのはプリフィックス v と小文字の l です。)

さあ、式を計算しましょう。

1:  633.94526  2:  633.94526  1:  67.23607
    .          1:  566.70919      .
                   .

 r 7 r 6 *      r 3 r 5 *         -

2:  67.23607   3:  67.23607   2:  67.23607   1:  0.52141679
1:  1862.0057  2:  1862.0057  1:  128.9488       .
    .          1:  1733.0569      .
                   .

 r 7 r 4 *      r 3 2 ^           -              /   t 8

これは傾き m の値です。 y切片 b は、今や簡単な次式で求められます。

b = (sum(y) - m sum(x)) / N
1:  13.613     2:  13.613     1:  -8.09358   1:  -0.425978
    .          1:  21.70658       .              .
                   .

   r 5            r 8 r 3 *       -              r 7 /   t 9

この直線近似 m x + b を「プロット」して、元のデータと比べてみましょう。

1:  [0.699, 0.735, ... ]    1:  [0.273, 0.309, ... ]
    .                           .

    r 1 r 8 *                   r 9 +    s 0

マッピングコマンドを使わなくても、リスト(ベクトル)に定数を掛けたり足したりできる事に 注意してください。これはベクトル代数では普通の演算です。 Calc にとってみれば、19次元空間の幾何学をやっていたに過ぎません。

このリスト(ベクトル)と元の y リスト(ベクトル)の差分を取って、 近似誤差の感触を得ることができます。最大の誤差を見つけてみましょう。

1:  [0.0387, 0.0112, ... ]   1:  [0.0387, 0.0112, ... ]   1:  0.0897
    .                            .                            .

    r 2 -                        V M A                        V R X

最初に差分リスト(ベクトル)を計算し、そして差分の絶対値を取りました。 そしてそのリスト(ベクトル)全体に、max 関数をまとめ作用させました。 (max 関数は 2キー・シーケンス f x にバインドされていますが、 リスト(ベクトル)演算において max を使うのはよくあることなので、 この局面では文字 XN も それぞれ maxmin として解釈されます。 一般に、 V MV R のプロンプトに対しては、 作用させたい関数を呼出すキー・シーケンスで答えます。 あなたの好みで V R f x でも V R x max RET でも 良かったのです。)

データの可視化

あなたのシステムに GNUPLOT があれば、 データと直線をグラフにして近似の具合を見ることができます。 (GNUPLOT 3.0 であればディスプレイの種類に依らず以下の説明どおりに動作します。 X-windows 以外での GNUPLOT 2.0 は、グラフを見るのに追加の操作が必要な場合が あります。)

オリジナルデータのプロットから始めましょう。 "x"リスト(ベクトル)と "y"リスト(ベクトル)をスタック上にリコールして g f を 押します。 この「速い」グラフ化コマンドは、 単なるデータプロットに必要な全ての操作を実行します。

2:  [1.34, 1.41, 1.49, ... ]
1:  [0.234, 0.298, 0.402, ... ]
    .

    r 1 r 2    g f

全てがうまく行けば、 すぐにデータがプロットされたグラフのウィンドウが現れます。 (もし出なければ、GNUPLOT か Calc の担当者に連絡して 何が悪かったのか調べてください。) X window システムでは、別個のグラフィックウィンドウとして現れます。 他の種類のディスプレイの場合、デフォルトでは Emacs 自身が粗い キャラクタ・グラフィックスを表示します。 キャラクタ・グラフィックスを見終わったら q を押してください。

次に、最小二乗法で得た近似直線を追加してみましょう。

2:  [1.34, 1.41, 1.49, ... ]
1:  [0.273, 0.309, 0.351, ... ]
    .

    DEL r 0    g a  g p

2番目のカーブのデータ点を強調表示することは大して役に立ちませんから、 g S g p とタイプして取り除きましょう。 GNUPLOT を終了してウィンドウを消すときは g q とタイプしてください。

(*) 練習問題 2. 以前の練習問題で、 一般の連立方程式における最小二乗法のやり方をお見せしました。 今回の 19個のデータ点はまさに、 それぞれの (x_i,y_i) に対応する 19個の y_i = m x_i + b 形式の方程式と言えます。 転置行列法を使って m および b について解き、 上の結果と比較しなさい。 リスト 練習問題 2 解答「行列による最小二乗近似」 参照 . (*)

(*) 練習問題 3. 入力データが長方形を形成していなかったら、 M-# g (calc-grab-region) をリージョンに対する通常動作で 機能させることができます。 これは改行をスペースと同じに扱いながら、左から右、上から下に読み取ります。 このコマンドをつかって、次の数の幾何平均を求めなさい。 (幾何平均とは、n 個の数値の積のn乗根です。)

2.3  6  22  15.1  7
  15  14  7.5
  2.5

M-# g コマンドは周囲のベクトルカッコの有無にかかわらず、 スペースまたはカンマで分割された数値を読み取ります。 リスト 練習問題 3 解答「幾何平均(Geometric mean)」 参照 . (*)

代数表現された関数のマッピング(無名関数)

もう1つの例として、二項係数の定理は二項係数の交互和 n-C-0 マイナス n-C-1 プラス n-C-2 ... n-C-n が常にゼロになると言います。 n=6 について、これを確かめましょう。

1:  [1, 2, 3, 4, 5, 6, 7]     1:  [0, 1, 2, 3, 4, 5, 6]
    .                             .

    v x 7 RET                     1 -

1:  [1, -6, 15, -20, 15, -6, 1]          1:  0
    .                                        .

    V M ' (-1)^$ choose(6,$) RET             V R +

V M ' コマンドは、 リスト(ベクトル)にマッピングする関数の代数的表現による入力を促します。 表現内部の記号 `$' は、関数に与える引数を表します。 Calc はこの関数をリスト(ベクトル)の各要素に作用させる際、 順番に各要素を `$' に代入して計算します。

2引数の関数を定義するには、 第1引数を `$$' で、第2引数を `$' で表します。 V M ' $$-$ RET は、V M - と同等です。 これは標準の代数的入力方式(`$$' はスタック top から2番目の項を表し、 `$' はスタック top の項を表し、 そして ' $$-$ RET- と全く同じ。)に似ています。

V M ' コマンドが2つの事項をトレイルに記録したことに注目してください。 1つは通常のような結果で、 もう1つはなにやら面白いものに `oper' と付いていて、 これは入力した演算子関数を表します。 関数は `< >' に囲まれて、また引数は `#' 記号で表現されます。 複数の引数がある場合は、`#1', `#2', ... のように表されます。 (例えば、V M ' $$-$ ではトレイルに `<#1 - #2>' を記録します。) このオブジェクトは"無名関数"であって、 あなたは V M ' のプロンプトに対して、 無名の `< >' 表記法を使うことができます。 無名関数表記は興味深い、時には有用な性質を持っていて、 無名関数は実際に使われるときまで評価されません。 例えば V M ' $+random(2.0) では `random(2.0)' がいったん評価され その乱数がリスト(ベクトル)要素にそれぞれ加算されますが、 V M ' <#+random(2.0)> ではリスト(ベクトル)の各要素ごとに個別に `random(2.0)' が評価されます。

V M と共に用いられる、 しばしば有用な他の演算子群は関係演算子です。 例えば a = は、2つの数値を比較して等しければ 1 を、 異なっていれば 0 を返します。同様に、 a < はある数値がもう1つより小さいかどうかを調べます。

その他有用なリスト(ベクトル)演算には次のようなものがあります。

`v v'
リスト(ベクトル)を端から端まで反転。
`V S'
リスト(ベクトル)要素を昇順にソート。
`v r', `v c'
行列から行または列をひとつ抽出。 または(どちらのコマンドも)単純リスト(ベクトル)から要素をひとつ抽出。 負の引数では行、列、リスト(ベクトル)要素をひとつ削除。

(*) 練習問題 4. k次の因子関数(divisor function) は、ある整数 n の全ての約数の k乗 の合計です。 適度に小さい数 n について、この因子関数の計算方法を考案しなさい。 テストとして、30 の 0次および 1次の因子関数はそれぞれ 8, 72 です。 リスト 練習問題 4 解答「因子関数(Divisor function)」 参照 . (*)

(*) 練習問題 5. k f コマンドは ある数の素因数のリストを生成します。ある数が square-free --- すなわち全ての素因数がリスト中に1回しか現れない -- かどうか知ることは しばしば重要です。square-free かどうか調べ、そうであれば 1 を、 そうでなければ 0 をスタックに置くキー・ストロークを考案しなさい。 リスト 練習問題 5 解答「重複因数」 参照 . (*)

(*) 練習問題 6. 下図のようなリストのリストを構築しなさい。 (v / コマンドで、ベクトルの複数行表示を行ってください。)

1:  [ [1],
      [1, 2],
      [1, 2, 3],
      [1, 2, 3, 4],
      [1, 2, 3, 4, 5],
      [1, 2, 3, 4, 5, 6] ]

リスト 練習問題 6 解答「三角リスト」 参照 . (*)

(*) 練習問題 7. 下図のようなリストのリストを構築しなさい。

1:  [ [0],
      [1, 2],
      [3, 4, 5],
      [6, 7, 8, 9],
      [10, 11, 12, 13, 14],
      [15, 16, 17, 18, 19, 20] ]

リスト 練習問題 7 解答「もうひとつの三角リスト」 参照 . (*)

(*) 練習問題 8. Bessel 関数 J1 のリスト `besJ(1,x)' を、 x について 0 から 5 まで 0.25 ステップで計算しなさい。 (計算した数値群から) `besJ(1,x)' が最大になる x をみつけなさい。 「自動的」な方法を使ってください。 単にリストを読んで最大値を見つけるようなのは駄目です。 (a X コマンドと言うのがあって、この種の自動的方法を行います。 数値解(Numerical Solutions) 参照 ) リスト 練習問題 8 解答「Bessel 関数の最大値」 参照 . (*)

(*) 練習問題 9. m=12 のとき、 0 <= N < 10^m なる整数(すなわち12桁以下の整数)が与えられているとします。 m 桁の、各要素が 0 から 9 のベクトルに変換しなさい。 桁位ベクトル表記において、この整数に1を足して m+1 桁のベクトルを作りなさい(最上位桁に繰上りが起こるかも知れないので)。 このベクトルを普通の整数に逆変換して戻しなさい。 やってみるべき適当な整数は、25129925999 です。 リスト 練習問題 9 解答「整数演算の機械的処理」 参照 . (*)

(*) 練習問題 10. 友人ジョーは、 リスト中の数値が全て等しいかどうかテストするために V R a = を使ってみました。どうなったでしょう ? あなたならどうやってこのテストを行いますか ? リスト 練習問題 10 解答「全要素の等価」 参照 . (*)

(*) 練習問題 11. 半径 1 の円の面積は pi です。 その円に外接する 2x2 の正方形の面積は 4 です。 ですから、この正方形に N 個のダーツをランダムに投げたら、 そのうち約 pi/4 が円内に刺さるでしょう。 これは pi の値を見積もる面白い方法を提供します。 k r コマンドは、ゼロとスタック値の間のランダムな値を生成します。 2.0 k r 1 - とタイプすれば、 -1 から 1 のランダムな浮動小数点値が得られることになります。 正方形内の 100 個のランダムな (x,y) 点からなるベクトルを作り、 マッピングとまとめ作用を使っていくつの点が単位円内に位置するか数えなさい。 ヒント: v b コマンドを使います。 リスト 練習問題 11 解答「ダーツでπを推定」 参照 . (*)

(*) 練習問題 12. マッチ棒問題pi を計算する別の方法です。 1 インチ間隔で縦線の引かれた無限に広い平面があるとしましょう。 平面に 1 インチのマッチ棒を落とします。 このときマッチ棒が縦線と交差して着地する確率は 2/pi になります。 pi を見積もるために、100個のマッチ棒を落としなさい。 (もっと楽しみたいならもうひとつ、 2 つの大きな整数のGCD(最大公約数:k g) が1になる確率は 6/pi^2 になります。 これは pi を見積もるための、もう1つの別の方法です。) リスト 練習問題 12 解答「マッチ棒でπを推定」 参照 . (*)

(*) 練習問題 13. 文字列をダブルクォートで囲って `"hello"' のように代数的入力すると、 文字の ASCII コードからなるベクトルを 生成します(この場合、[104, 101, 108, 108, 111])。 文字列のハッシュコード(文字列の値を表す整数)を計算すると 便利な場合があります。 等しい文字列は同じハッシュコードを持ち、 異なった文字列はおそらく異なったハッシュコードを持っています。 (例えば、 Calc は 400 以上の関数名を持っていますが、 Emacs は関数群をそのハッシュコードごとに「バケツに入れて」分類しているので、 どんな関数名でもその定義を速く見出すことができます。 たまに複数の名前が同じバケツの中にハッシュされますが、 すべての名前の中から検索するより少数の名前の中から検索する方が容易です。) よくあるハッシュ関数は次のように計算されます。 まず h = 0 に設定します。次に 文字列から順番に取出した文字ごとに h = 3h + c_i とします。 ただし c_i は文字の ASCII コードです。 511個のバケツがある場合は、ハッシュコードの 511 の剰余を取りバケツ番号とします。 文字列ベクトルをハッシュコードに変換する、 簡単なコマンドあるいはコマンド群を開発してください。 `"Testing, 1, 2, 3"' のハッシュコードは 1960915098 で、 511 の剰余は 121 です。 リスト 練習問題 13 解答「ハッシュコード」 参照 . (*)

(*) 練習問題 14. H V RH V U は ネストされた関数評価を行います。 H V U は初期値と ステップ数 n をスタックから取り、 それから与えた関数を初期値に 0, 1, 2, ... n 回作用させ、 結果を並べたベクトルを返します。このコマンドを使い、 「ランダムウォーク」50 歩分を作りなさい。 2次元座標 (0,0) からスタートし、xy について -1 から 1 までのランダムな距離分移動して一歩とし、 そして次の一歩というように続けます。 g f コマンドでこのランダムウォークを表示しなさい。 次にランダムウォークの一歩が単位長で勝手な方向になるように改良しなさい。 (ヒント: sincos 関数はある角度のコサインとサインのベクトルを返します。) リスト 練習問題 14 解答「ランダムウォーク」 参照 . (*)


Go to the first, previous, next, last section, table of contents.     利用度数