[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『ベクトル積のユーザー関数をつくりたい』(シムニンゲン)
はじめまして、仕事上で技術計算をしているおやじです。
二つのベクトルの掛け算でベクトル積(外積)というのがあります。
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を選択し、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型の計算精度の件、安心しました。 たいへん勉強になりました、今後もよろしくお願いします。
(シムニンゲン)
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.