C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

C++からVBへの変換 [1]

[トピック内 27 記事 (21 - 27 表示)]  << 0 | 1 >>

■91939 / inTopicNo.21)  Re[17]: C++からVBへの変換
  
□投稿者/ えんえん (15回)-(2019/08/12(Mon) 14:12:33)
ありがとうございます。

以下のようにしてみましたが
やはりうまくいきませんでした。


    Public Sub one_D_FFT(ByRef xr#(), ByRef xi#())


        Dim n As Integer = xr.GetLength(0) - 1

        Dim theta = 2 * Math.PI / n

        Dim xr2 As New ArraySegment(Of Double)(xr), xi2 As New ArraySegment(Of Double)(xi)

        Dim tmpr(n) As Double, tmpi(n) As Double
        Dim tmpr2 As New ArraySegment(Of Double)(tmpr), tmpi2 As New ArraySegment(Of Double)(tmpi)

        Call fft(n, theta, xr2, xi2, tmpr2, tmpi2)


    End Sub




    ' Private Sub fft(ByVal n%, ByVal theta#, ByRef ar As ArraySegment(Of Double), ByRef ai As ArraySegment(Of Double), ByRef tmpr As ArraySegment(Of Double), ByRef tmpi As ArraySegment(Of Double))
    Private Sub fft(ByVal n%, ByVal theta#, ByVal ar As ArraySegment(Of Double), ByVal ai As ArraySegment(Of Double), ByVal tmpr As ArraySegment(Of Double), ByVal tmpi As ArraySegment(Of Double))

        If n <= 1 Then Exit Sub

        Dim radix As Integer = 2
        Do While radix * radix <= n
            If n Mod radix = 0 Then Exit Do
            radix += 1
        Loop

        If n Mod radix <> 0 Then radix = n

        Dim n_radix As Integer = n \ radix

        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                Dim xr As Double = ar.Array(j)
                Dim xi As Double = ai.Array(j)

                For r As Integer = n_radix To n - 1 Step n_radix

                    Dim wr0 As Double = Math.Cos(theta * m * r)
                    Dim wi0 As Double = Math.Sin(theta * m * r)
                    xr += wr0 * ar.Array(r + j) - wi0 * ai.Array(r + j)
                    xi += wr0 * ai.Array(r + j) + wi0 * ar.Array(r + j)

                Next r


                Dim wr As Double = Math.Cos(theta * m * j)
                Dim wi As Double = Math.Sin(theta * m * j)
                tmpr.Array(tmpr.Offset + m * n_radix + j) = xr * wr - xi * wi
                tmpi.Array(tmpi.Offset + m * n_radix + j) = xi * wr + xr * wi

            Next m

        Next j

        For r As Integer = 0 To n - 1 Step n_radix

            fft(n_radix, theta * radix, New ArraySegment(Of Double)(tmpr.Array, tmpr.Offset + r, tmpr.Count - r), New ArraySegment(Of Double)(tmpi.Array, tmpi.Offset + r, tmpi.Count - r), ar, ai)

        Next r



        For j As Integer = 0 To n_radix - 1

            For m As Integer = 0 To radix - 1

                ar.Array(ar.Offset + radix * j + m) = tmpr.Array(n_radix * m + j)
                ai.Array(ai.Offset + radix * j + m) = tmpi.Array(n_radix * m + j)

            Next m

        Next j


    End Sub




> において、「y.Array」や「z.Array」は、配列 x への参照を意味します。

> なので、x(3) = 100 と代入すれば、z(1) も同様に 44 から 100 へと変化します。

「y.Array」や「z.Array」を書き換えた時
元の配列xも同時に変化してくれないといけないのではないのでしょうか?
元の配列xは変化しない方が良いという意味ですか?


> それと、むやみに ByRef を使わないでください。
> 今回は出力引数が存在しないため、ByRef の出番は一切ありません。


fftを再帰的に呼び出しているわけですが、
この場合、配列を書き換えて元のサブルーチンに返しているわけなので、
参照型である必要があるのではないでしょうか?
もっとも配列の場合、ByValを指定したところで、ByRef扱いになると思いますが
間違っていますか?

もし、出力引数がなければ、
再帰処理の意味がなくなってしまうと思うのですが・・・



どうやれば良いか分からないので、
できれば、最終回答をお教えいただけないでしょうか?
恐らく、こうやって何度もやりとりするよりも
効率的だと思うのですが・・・

一度、回答をいただけるとステップ実行しながら
自分の間違いに気づけると思います




引用返信 編集キー/
■91940 / inTopicNo.22)  Re[18]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2298回)-(2019/08/12(Mon) 16:25:23)
No91939 (えんえん さん) に返信
No91939 (えんえん さん) に返信
> 以下のようにしてみましたが
> やはりうまくいきませんでした。

同じ移植ミスを、他の行でも繰り返していますよ。

Dim z As ArraySegment(Of T) における、
z(0) と z.Array(0) の違いをもう一度見直してみましょう。


>> において、「y.Array」や「z.Array」は、配列 x への参照を意味します。
>> なので、x(3) = 100 と代入すれば、z(1) も同様に 44 から 100 へと変化します。
> 「y.Array」や「z.Array」を書き換えた時
> 元の配列xも同時に変化してくれないといけないのではないのでしょうか?

はい。ご認識の通り、連動して変化します。
そして先のそれは「変化することを理解していただくための説明コード」として記載したものです。


No91938 の説明コードにおいて、読み取り専用の z(1) が x(3) と同じ物であり、
それが z.Array(z.Offset + 1) を通じて書き換えられることを理解できていれば、
>>> Dim xr As Double = ar.Array(j)

>>> xr += (wr0 * ar.Array(r + j) - wi0 * ai.Array(r + j))

>>> tmpi.Array(m * n_radix + j) = xi * wr + xr * wi

>>> ai.Array(radix * j + m) = tmpi.Array(n_radix * m + j)
といったミスをおこさずに済んだかと思います。


あるいはこの移植ミスが理解不足によるものでもなく、
単なるうっかりミスだったのであれば、「ステップ実行」して
配列の内容を確認しながら動作チェックしていれば、
その間違いに気づけたかもしれません。


いずれにせよ、毎回、Offset を考慮したコードを書くのは移植ミスを引き起こしやすいので、
元の C/C++ コードに近い記述にできるよう、何らかのヘルパーメソッドを
設けることをおすすめしておきます。 No91917 のような拡張メソッドのように。


> 元の配列xは変化しない方が良いという意味ですか?

変化させる必要があるからこそ、ArraySegment の利用を提案した次第です。
変化しないと再帰処理させられないという点は、 No91913 の冒頭でも述べていますね。


ArraySegment を使わない場合は、御自身で書かれた r_st% のように、
Offset に相当する引数をそれぞれの配列に対して用意する案が使えます。

配列が 4 つあるので、本来であれば管理すべき添字も 4 つ分必要ですが、
今回のケースでは tmp 版か否かで 2 系統あれば十分かと思います。
(複素数の虚数部と実数部は同じ添字管理で済むため)


> fftを再帰的に呼び出しているわけですが、
> この場合、配列を書き換えて元のサブルーチンに返しているわけなので、
> 参照型である必要があるのではないでしょうか?

「『参照型』と『値型』の違い」の話と、
「『参照渡し(ByRef)』と『値渡し(ByVal)』違い」の話が
まだごっちゃになっているように見えます。


> もっとも配列の場合、ByValを指定したところで、ByRef扱いになると思いますが
> 間違っていますか?

間違っています。


今回の『void fft(int n, double theta, double ar[], double ai[], double tmpr[], double tmpi[])』において、
配列部は double* 型のポインタと認識されますが、
 theta = 新しいdouble値;
 ar = 新しいdouble配列を指すポインタ;
 ai = 新しいdouble配列を指すポインタ;
 tmpr = 新しいdouble配列を指すポインタ;
 tmpi = 新しいdouble配列を指すポインタ;
という処理は一切出てきません。

あるのは、
 tmpr[何某] = 新しいdouble値;
 tmpi[何某] = 新しいdouble値;
 ar[何某] = 新しいdouble値;
 ai[何某] = 新しいdouble値;
だけですよね。つまり、これらの引数は「出力引数」ではありません。


同様に、今回記載された No91939 においても、
仮引数に対して、新しい配列や ArraySegment を割り当てている個所はどこにもないですよね。
実引数に対して、新しい配列や ArraySegment を割り当てている個所ならばありますけれども。
引用返信 編集キー/
■91941 / inTopicNo.23)  Re[18]: C++からVBへの変換
□投稿者/ 774RR (728回)-(2019/08/12(Mon) 16:56:43)
> できれば、最終回答をお教えいただけないでしょうか?
それはあなただけに都合のいい話ですよね

「正解をさっさと伝えるなんて指導者の怠慢さ」
「自分で見つけた答えなら一生忘れない」
from あおあし

自分で理解することを頑張ってみて、それでも理解できなきゃ
この業界が自分向けでない
ってことでリタイアするもアリでしょ

引用返信 編集キー/
■91942 / inTopicNo.24)  Re[18]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2299回)-(2019/08/12(Mon) 17:33:28)
No91939 (えんえん さん) に返信
> できれば、最終回答をお教えいただけないでしょうか?
> 恐らく、こうやって何度もやりとりするよりも
> 効率的だと思うのですが・・・

申し訳ありませんが、現状は言語文法的な側面からしか回答できません。

自分は高速フーリエ変換について正確に理解しているわけではないため、
「作成依頼」に近い「質問」をされても、デバッグすることができないためです。


実引数としてどういう値を入れた時に、なんという結果が返されるのか示す、
ブラックボックステストのための情報があるわけでも無いですし。



> もし、出力引数がなければ、
> 再帰処理の意味がなくなってしまうと思うのですが・・・

今回は ByVal で十分でしょう。

改めて、 No91906 の最後の個所にあるコードで説明してみます。
今回は意図的に ByVal を付けておきます。

  Sub Test4_Modify(ByVal a As ArraySegment(Of Double))
    a.Array(a.Offset + 1) *= 10
  End Sub

このコードは「ByVal a As ArraySegment(Of T)」という宣言になっていますが、
呼び出し元の値は、きちんと 10 倍になっています。


・ArraySegment(Of T) 構造体は値型。

・T が値型であれ参照型であれ、配列 T() は列は常に「参照型」。

・a.Array プロパティが返す型は As T() なので「参照型」。

・a.Array プロパティは ReadOnly であり、書き換えられない。
 たとえば「a.Array = Nothing」などと書くことはできない。

・a.Array プロパティから返される「一次元配列」は、
 ReadOnly というわけではないので、その要素を書き換えることができる。
 たとえば「a.Array(0) = Nothing」や「a(1) *= 10」と書いても良い。



==== 入力引数の例 <In> === ※いずれも ByVal で書いている。

 Sub Method1(ByVal x() As Integer)
   x(0) = 100 ' 呼び出し元の実引数では、配列の先頭要素が 100 に替わる。ただし、配列そのものが指しかわるわけでは無い。
 End Sub

 Sub Method2(ByVal lbl As Label)
   lbl.Text = CStr(Now) ' 呼び出し元で指定した Label のテキストが変化する。ただし、Label 型変数が別のラベルを指し示すわけでは無い。
 End Sub

 Sub Method3(ByVal pt As Point)
   Console.WriteLine("{0}, {1}", pt.X, pt.Y)
 End Sub


==== 出力引数の例 <Out> === ※いずれも ByRef で書いている。

 Sub Method4(ByRef x() As Integer)
   x = New Integer() { 0, 1, 2, 3 } ' 呼び出し元が渡した変数の内容は、新しい配列の参照へと差し替わる。
 End Sub

 Sub Method5(ByRef f As Form)
   f = New Form2() ' 呼び出し元が渡した変数の内容は、Form2 への参照へと書き換わる。
 End Sub

 Sub Method6(ByRef i As Integer)
   i = 12345 ' 呼び出し元が渡した変数の内容は、12345 という値に書き換わる。
 End Sub

 Sub Method7(ByRef pt As Point)
   pt = New Point(-1, 1) ' 呼び出し元が渡した変数の内容は、座標 (-1, 1) という値に置き換わる。
 End Sub


==== 入出力引数の例 <In, Out> === ※いずれも ByRef でなければならない。

 Sub Method8(ByRef x As Integer)
   x += 1
 End Sub

 Sub Method9(ByRef pt As Point)
   pt.Offset(-1, 1)
 End Sub

 Function Method10(ByVal x As Point) As Point
   pt.Offset(-1, 1)
   Return pt
 End Sub
引用返信 編集キー/
■91944 / inTopicNo.25)  Re[14]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2300回)-(2019/08/12(Mon) 22:51:04)
No91932 (774RR さん) に返信
> FFT なんぞテキトーに探せばいくらでもライブラリが転がっているような気のせいがする。

下記によれば、Math.NET Numerics で処理できるっぽいです。
https://tomosoft.jp/design/?p=9127
https://www.nuget.org/packages/MathNet.Numerics/

MathNet.Numerics.IntegralTransforms 名前空間の Fourier クラスが、
 Public Shared Sub Forward(ByVal samples() As System.Numerics.Complex)
 Public Shared Sub Forward(ByVal samples() As System.Numerics.Complex, ByVal options As FourierOptions)
Public Shared Sub Forward(ByVal real() As Double, ByVal imaginary() As Double, Optional ByVal options As FourierOptions = FourierOptions.Default)
などのメソッドを持っていました。


Double 配列から、虚数データ無しの複素数構造体配列に変換する場合はこんな感じ。
 Dim rawData() As Double = 元データ
 Dim samples() As Complex = rawData.Select(Function(d) New Complex(d, 0#)).ToArray()


options 引数はフーリエ変換規則を示す物らしいですが…今回の目的に合致するものがあるかは分かりませんでした。

Dim options As FourierOptions
options = FourierOptions.Default '0: 普遍的; 対称スケーリングおよび共通指数(Maple で使用)
options = FourierOptions.AsymmetricScaling '2: 逆方向に 1/N だけスケーリング; 順方向のスケーリングなし
options = FourierOptions.Matlab '2: 逆方向に 1/N だけスケーリング; 順方向のスケーリングなし(Mathlab で使用) [AsymmetricScaling と同じ]
options = FourierOptions.NoScaling '4: 何もスケーリングしない (順方向変換でも逆方向変換でもない)
options = FourierOptions.InverseExponent '1: 逆被積分指数 (順方向:正符号、逆方向:負符号)
options = FourierOptions.NumericalRecipes '5: 逆被積分指数; 何もスケーリングしない(すべての数値レシピベースの実装で使用)[= InverseExponent Or NoScaling]
引用返信 編集キー/
■91946 / inTopicNo.26)  Re[17]: C++からVBへの変換
□投稿者/ 魔界の仮面弁士 (2302回)-(2019/08/13(Tue) 01:00:13)
No91938 (魔界の仮面弁士) に追記
>  Dim x() As Byte = New Byte(5) {11, 22, 33, 44, 55, 66}
>  Dim y As New ArraySegment(Of Byte)(x)
>  Dim z As New ArraySegment(Of Byte)(x, 2, 3)
> において、「y.Array」や「z.Array」は、配列 x への参照を意味します。
> なので、x(3) = 100 と代入すれば、z(1) も同様に 44 から 100 へと変化します。

補足説明。

System.ArraySegment(Of T) は、.NET Framework 2.0 以上で使用可能な、比較的歴史の長い型ですが、
「 z(1) 」のようにインデクサで参照するためには、.NET Framework 4.5 以上が必要となります。

.NET Framework 4 以下の場合、LINQ 操作にさえ対応していないのでご注意ください。




No91942 (魔界の仮面弁士) に追記
>> できれば、最終回答をお教えいただけないでしょうか?
> 申し訳ありませんが、現状は言語文法的な側面からしか回答できません。


ということで、文法的に変換しただけで、コンパイルすらしていないコードですが、
一応、当方で翻訳してみたものを掲載してみます。

C/C++ はそもそも専門外ですし、FFT の理論も全く理解しておらず、おまけに
どんな値を渡せばよいのか、そしてどういう結果になるべきかというテスト要件すら不明なので、
およそ「最終回答」と呼ぶには程遠いコードではありますが、一応参考までに。



'void fft(int n, double theta, double ar[], double ai[], double tmpr[], double tmpi[])
'{
Private Sub fft(ByVal n%, ByVal theta#, ByVal ar As ArraySegment(Of Double), ByVal ai As ArraySegment(Of Double), ByVal tmpr As ArraySegment(Of Double), ByVal tmpi As ArraySegment(Of Double))

  'if (n <= 1) return;
  If n <= 1 Then Exit Sub

  '/* ---- factorization ---- */
  'for (radix = 2; radix * radix <= n; radix++) {
  '  if (n % radix == 0) break;
  '}
  Dim radix As Integer = 2
  Do While radix * radix <= n
    If n Mod radix = 0 Then Exit Do
    radix += 1
  Loop

  'if (n % radix != 0) radix = n;
  'n_radix = n / radix;
  If n Mod radix <> 0 Then radix = n
  Dim n_radix As Integer = n \ radix

  '/* ---- butterflies ---- */
  'for (j = 0; j < n_radix; j++) {
  '  for (m = 0; m < radix; m++) {
  For j As Integer = 0 To n_radix - 1
    For m As Integer = 0 To radix - 1

      'xr = ar[j];
      'xi = ai[j];
      '==> ここから修正案の新コード
      Dim xr As Double = ar(j)
      Dim xi As Double = ai(j)
      '=== ここから No91939 の旧コード
      'Dim xr As Double = ar.Array(j)
      'Dim xi As Double = ai.Array(j)
      '<==


      'for (r = n_radix; r < n; r += n_radix) {
      For r As Integer = n_radix To n - 1 Step n_radix

        'wr = cos(theta * m * r);
        'wi = sin(theta * m * r);
        Dim wr0 As Double = Math.Cos(theta * m * r)
        Dim wi0 As Double = Math.Sin(theta * m * r)

        'xr += wr * ar[r + j] - wi * ai[r + j];
        'xi += wr * ai[r + j] + wi * ar[r + j];
        '==> ここから修正案の新コード
        xr += wr0 * ar(r + j) - wi0 * ai(r + j)
        xi += wr0 * ai(r + j) + wi0 * ar(r + j)
        '=== ここから No91939 の旧コード
        xr += wr0 * ar.Array(r + j) - wi0 * ai.Array(r + j)
        xi += wr0 * ai.Array(r + j) + wi0 * ar.Array(r + j)
        '<==

      '}
      Next r

      'wr = cos(theta * m * j);
      'wi = sin(theta * m * j);
      Dim wr As Double = Math.Cos(theta * m * j)
      Dim wi As Double = Math.Sin(theta * m * j)

      'tmpr[m * n_radix + j] = xr * wr - xi * wi;
      'tmpi[m * n_radix + j] = xi * wr + xr * wi;
      tmpr.Array(tmpr.Offset + m * n_radix + j) = xr * wr - xi * wi
      tmpi.Array(tmpi.Offset + m * n_radix + j) = xi * wr + xr * wi

    '}
    Next m

  '}
  Next j


  'for (r = 0; r < n; r += n_radix) {
  '  fft(n_radix, theta * radix, &tmpr[r], &tmpi[r], ar, ai);
  '}
  For r As Integer = 0 To n - 1 Step n_radix
    '==> ここから修正案の新コード (要 No91917 の拡張メソッド)
    fft(n_radix, theta * radix, tmpr.AsSegment(r), tmpi.AsSegment(r), ar, ai)
    '=== ここから No91939 の旧コード
    fft(n_radix, theta * radix, New ArraySegment(Of Double)(tmpr.Array, tmpr.Offset + r, tmpr.Count - r), New ArraySegment(Of Double)(tmpi.Array, tmpi.Offset + r, tmpi.Count - r), ar, ai)
    '<==
  Next r

  'for (j = 0; j < n_radix; j++) {
  '  for (m = 0; m < radix; m++) {
  '    ar[radix * j + m] = tmpr[n_radix * m + j];
  '    ai[radix * j + m] = tmpi[n_radix * m + j];
  '  }
  '}
  For j As Integer = 0 To n_radix - 1
    For m As Integer = 0 To radix - 1
      '==> ここから修正案の新コード (要 No91917 の拡張メソッド)
      ar.SetValue( radix * j + m, tmpr(n_radix * m + j) )
      ai.SetValue( radix * j + m, tmpi(n_radix * m + j) )
      '=== ここから No91939 の旧コード
      ar.Array(ar.Offset + radix * j + m) = tmpr.Array(n_radix * m + j)
      ai.Array(ai.Offset + radix * j + m) = tmpi.Array(n_radix * m + j)
      '<==
    Next m
  Next j

'}
End Sub
引用返信 編集キー/
■91947 / inTopicNo.27)  Re[15]: C++からVBへの変換
□投稿者/ えんえん (16回)-(2019/08/13(Tue) 11:01:38)
ありがとうございます。

うまくいきました
ばっちりでした!!!!!!

今から、自分のコードで何が間違っていたか確認したいと思います。

ちなみに

> 下記によれば、Math.NET Numerics で処理できるっぽいです。


Math.NET Numerics などのライブラリでFFTが実装されるのは知っていますし
使ったこともあります。
ただ、こうしたライブラリは基数が2のべき乗にしか対応していないのです。

任意の基数に対応したFFTのライブラリってなぜか見つからないのです。



引用返信 編集キー/

このトピックをツリーで一括表示

<前の20件
トピック内ページ移動 / << 0 | 1 >>

このトピックに書きこむ