[[20040723001655]] 『ベクトル積のユーザー関数をつくりたい』(シムニンゲン) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『ベクトル積のユーザー関数をつくりたい』(シムニンゲン)

はじめまして、仕事上で技術計算をしているおやじです。
二つのベクトルの掛け算でベクトル積(外積)というのがあります。
A1:A3にベクトルAのx,y,z成分Ax,Ay,Azがあり、同様に
B1:B3にベクトルBのx,y,z成分Bx,By,Bzがあるとき、
ベクトル積A×Bの結果である
Cx=Ay*Bz-Az*By
Cy=Az*Bx-Ax*Bz
Cz=Ax*By-Ay*Bx
をC1:C3に書き込めるような配列関数をつくりたいのですが、
配列を引数にして配列を返す関数の書き方がわかりません。
達人の皆様のお知恵を貸してください。
WindowsXP上のExcel2002を使っております。


一応ユーザー定義関数ということで作ってはみましたが、
とても拙いですので、どなたか、訂正してくださればと思います。
(配列関数とありますが、配列数式での回答希望でしたでしょうか?)

Function Vector(Ax, Ay, Az, Bx, By, Bz, D) 'Dはdimension(次元)

    Cx = Ay * Bz - Az * By
    Cy = Az * Bx - Ax * Bz
    Cz = Ax * By - Ay * Bx

    If D = 1 Then
        Vector = Cx
    ElseIf D = 2 Then
        Vector = Cy
    ElseIf D = 3 Then
        Vector = Cz
    Else
        Vector = "error"
    End If

End Function

 使い方ですが、
 セルC1:  =vector(a1,a2,a3,b1,b2,b3,1)
 セルC2:  =vector(a1,a2,a3,b1,b2,b3,2)
 セルC3:  =vector(a1,a2,a3,b1,b2,b3,3)
 としてください。
 関数は一つの値を返すので、
 最後の引数が、1ならばCxを、2ならばCyを、3ならばCzを返すようにしています。

 せめて、=vector(a1:a3,b1:b3,1)のような感じで使えるといいとは思ったのですが、
 a1:a3で受けた後、どう処理すればいいのか、私にはわからなかったので手放しました。

(まこ)


まこさん、ありがとうございます。
配列数式が正しいのですね。
おっしゃるとおり、C1:C3を選択して、
=vector(A1:A3,B1:B3) と入力し、Ctrl+Shift+Return
というような関数をめざしています。
Housakaさんのページ
http://hp.vector.co.jp/authors/VA016119/hajimete/udf2.html
で配列を引数にして配列を返すサンプルをみつけました。
が・・・、よく理解できないのでヒントでもいただければ幸いです。
(シムニンゲン)


サンプルを見つけてくださり、ありがとうございます。
ほとんど丸写しで作成しました。
3次元のみの外積をとるものと仮定しています。
n次元の外積へは適宜拡張してください。

そして、使い方は、範囲c1:c3を選択し、c1に=gaiseki(a1:a3,b1:b3)と入力して、ctrl+shift+enterとしてください。

しかしながら正しい結果を返しません!!
内部的には、ベクトルa,b,cの値を表示しているのですが、
セルへは正しく反映されません。

皆様、訂正方法をお教えください。よろしくお願いいたします。
なお、Excel2003,WindowsXPです。

Function gaiseki(VectorA As Variant, VectorB As Variant) As Variant

    '型宣言
    Dim a() As Double   'VectorA
    Dim b() As Double   'VectorB
    Dim c() As Double   'VectorC
    Dim x() As Long     '次元を表すindex 未使用
    Dim r As Range

    Dim i, j, k, n As Long

    'エラー処理
    On Error GoTo ErrorHandler
    gaiseki = CVErr(xlErrValue)

    '引数VectorAの値を、配列a()に格納
    'セルA1の値を配列a(1)に、セルA2の値を配列a(2)に、セルA3の値を配列a(3)にセットする。
    '配列の添字が1ならば、x=1次元、2ならば、y=2次元、3ならば、z=3次元を示す。
    If IsObject(VectorA) Then
        Set r = VectorA.Areas(1).Cells
        n = r.Count
        ReDim a(1 To n), b(1 To n), c(1 To n), x(1 To n)
        For i = 1 To n
            a(i) = r(i).Value
        Next
    ElseIf IsArray(VectorA) Then
        n = UBound(VectorA) - LBound(VectorA) + 1
        ReDim a(1 To n), b(1 To n), c(1 To n), x(1 To n)
        i = 1
        For j = LBound(VectorA) To UBound(VectorA)
            a(i) = VectorA(j, 1)
            i = i + 1
        Next
    Else
        n = 1
        ReDim a(1 To n), b(1 To n), c(1 To n), x(1 To n)
        a(1) = VectorA
    End If
    '引数VectorBの値を、配列a()に格納
    'セルB1の値を配列b(1)に、セルB2の値を配列b(2)に、セルB3の値を配列b(3)にセットする。
    If IsObject(VectorB) Then
        Set r = VectorB.Areas(1).Cells
        n = r.Count
        For i = 1 To n
            b(i) = r(i).Value
        Next
    ElseIf IsArray(VectorB) Then
        n = UBound(VectorB) - LBound(VectorB) + 1
        i = 1
        For j = LBound(VectorB) To UBound(VectorB)
            b(i) = VectorB(j, 1)
            i = i + 1
        Next
    Else
        n = 1
        b(1) = VectorB
    End If

    '外積の計算を行う。
    'Cx = Ay * Bz - Az * By
    'Cy = Az * Bx - Ax * Bz
    'Cz = Ax * By - Ay * Bx
    'ここでは3次元ベクトルの外積であり、n次元には非対応
    'また、2次元ベクトルと3次元ベクトルベクトルの外積を求めようとした場合などの
    'エラーチェックはしていない。
    c(1) = a(2) * b(3) - a(3) * b(2)
    c(2) = a(3) * b(1) - a(1) * b(3)
    c(3) = a(1) * b(2) - a(2) * b(1)

    '計算結果の確認用
    For i = 1 To 3
        MsgBox "a(" & i & ")=" & a(i)
        MsgBox "b(" & i & ")=" & b(i)
        MsgBox "c(" & i & ")=" & c(i)
    Next i

    '値を返す
    gaiseki = c
    MsgBox "無事に終了しました"
    Exit Function

ErrorHandler:

    MsgBox "エラーのため終了しました"
    Exit Function

End Function


こんにちは

 3次ベクトルのみ、
 ベクトルデータはA1,A2,A3の様に縦に並んでいること、
 結果もC1,C2,C3の様に縦に並んでいること の限定付きです。

 Function Vec(A As Variant, B As Variant) As Variant
    Dim C(1 To 3, 1 To 1) As Variant

    C(1, 1) = A(2, 1) * B(3, 1) - A(3, 1) * B(2, 1)
    C(2, 1) = A(3, 1) * B(1, 1) - A(1, 1) - B(3, 1)
    C(3, 1) = A(1, 1) * B(2, 1) - A(2, 1) * B(1, 1)
    Vec = C

 End Function

Variantで返す配列を2次元配列にするとうまく行くと思います。

 (ni)


   お名前が無いのですが、おそらくまこさんですね。ありがとうございます。
 計算結果の確認の部分など、たいへん参考になります。
 私もHousakaさんのサンプルをまねして格闘していましたが、
 まともにできませんでした。

   niさん、どうもありがとうございます。これ完璧です。
 まさに私がほしかったものです。涙涙の大感謝です。
 ついでといってはなんですが、なぜ2次元で受けて、
 2次元で返さなければいけないのか、解説していただけると
 うれしく思います。もうひとつ、技術計算なので倍精度で
 計算したいのですが、このように Variantを使っても、Double
 の精度は保たれているのでしょうか?

   実は外積の計算だけなら、最初にまこさんの書かれた配列を
 使わない方式でも十分であり、同様の関数を自作しておりました。
 目標は複素数の成分を持つ行列の掛け算と逆行列を求めることで、
 (そんなもんエクセルでやるなよ、というツッコミは無しで)
 今回教えていただいたものを元に、挑戦してみます。

 (シムニンゲン)


VBAでVariant型の変数に、セル範囲の値を一気に読み込む方法があります。

 Dim A As Variant
 A=Range("A1:C10").Value

 このとき、Variantで宣言したAには、2次元の配列でA1:C10の値が入力されます。
これを、

 Dim A As Variant
 A=Range("A1:A10").Value

 のように1列のデータを指定しても、Aはやはり2次元配列になります。
 (ステップ実行やブレークポイントでとめて、ウオッチウインドウで確認してみてください)
 受け取ったAが、配列か、そうでないかで単一の値、複数範囲を判断して、
 複数範囲だったら2次元配列のサイズで、何行、何列のデータか、判断できます。

 同様に、配列をセルに書き込むときも、2次元配列を渡してやります。

 Dim i As Long,A(1 to 10,1 to 1) As Variant
 For I=1 To 10
     A(i,1)=i
 Next
 Range("D1:D10").Value=A

 この例では、10行1列なので、A(1 to 10,1 to 1)ですが、
 n行m列なら、A(1 to n,1 to m)の大きさの配列を返せばいいです。

 なぜ2次元配列にしなければならないか、というのはよくわかりません。
 書き出すときは、Double型の2次元配列でもよかったように思います。

 Variant型の精度ですが、小数点演算が入ると、内部的にはDoubleになるはずです
 から、特に心配いらないと思います。
 Variant型を使うと遅いとか、2次元配列は遅いとか耳にしますが、
 行列積ぐらいの計算なら、わざわざ1次元Double型に変換するより、
 そのままの方が早いようです。
 (ni)


ni様、懇切な解説をいただきましてありがとうございます

 3次元ベクトルでも3行1列の行列として扱えば良い、という感じですね。
 Variant型の計算精度の件、安心しました。
 たいへん勉強になりました、今後もよろしくお願いします。

 (シムニンゲン)


function gaiseki・・・ 名前書き忘れてしまいました(^^ゞ
無事に解決したようで、よかったです。
また、niさんありがとうございます。
大変勉強になりました。感謝です。(まこ)


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.