こんにちは。タイトルが少々分かりにくくてすいません。
プログラミングで、どう実現したらいいのか分からない処理があります。
まず、任意の配列Aがあります。ここでは倍精度実数の2次元配列とし、A(i,j)と表します。
その2次元配列の各要素を使って、計算を行いたいものとします。
ここでは単純に、全ての成分を足し集める計算を考えます。
つまり∑(A(i,j))を求めたいということです。
ここで、同様に倍精度実数2次元配列のB(i,j)があるとします。
このB(i,j)に関しても∑(B(i,j))を求めたい時、当然この処理をサブルーチン(またはユーザー定義関数)としてまとめたいと思います。
ですが、このやり方がよく分かりません。
計算したい配列を、サブルーチンの引数にする必要があると思います。
しかしサブルーチン内で具体的に処理を記述する部分で、どうしたらいいのか分かりません。
A(i,j)を足し集める、というループ処理を書いたら、B(i,j)が引数の時には対応出来ません。
内部にC(i,j)というローカル変数を作って、A(i,j)もしくはB(i,j)を内部でC(i,j)に渡してから、C(i,j)を足し集める計算をするのが一つの手でしょうか?
しかしこのサブルーチンが頻繁に繰り返し呼び出される場合、毎度メモリーコピーの作業が生じるのは
処理が遅くなってしまうのでは?という不安があります。
ポインタを使えばいいのでしょうか?引数では配列のアドレスを引っ張ってきて、そのアドレスに対して処理をする・・・というような感じでしょうか。
お恥ずかしながらこれまでポインタを使ったことがないため、これで出来るのかが分かりません。
今回は単純な成分の足し算という例でしたが、もう少し具体的には、
二次元配列の成分i,jが座標を表し、そこに収納されている値を使って、いろいろな計算を行いたいということです。
その計算が頻繁に行われるため、出来るだけスマートかつ高速に動作するものを作りたいと思っています。
ちなみに言語はFortran90/95で、ちょっとマニアックですが、恐らく概念自体は他の言語でも同じだと思うので、Fortranが分からない方でも構いませんのでアドバイスをお願いします。
もしFortranが分かる方がいらっしゃれば、今回の例において、具体的なサンプルプログラムをいただけると非常に助かります。もちろん、無くても構いませんのでよろしくお願いします。
A 回答 (4件)
- 最新から表示
- 回答順に表示
No.4
- 回答日時:
#2です。
>書き忘れで申し訳ないのですが、実は配列A,Bはサイズが異なる可能性もあります。
>ただ、サイズは明示的に定義しますので、サブルーチンの引数として配列サイズを渡すことは可能です。
>このような場合にも、似たアルゴリズムで同じことの実現は可能でしょうか?
サイズの異なる2次元配列を処理するサブルーチン(sub3)をFortranで作ってみました。
5,2の配列と3,6の配列をsub3に渡し、その内容を印字しています。
(配列の渡し方と受け取り方が、この課題なので値の和は求めていません。)
------------------------------------------
program c120622
integer, parameter :: r_lim=5
integer, parameter :: c_lim=2
integer, parameter :: r_lim2=3
integer, parameter :: c_lim2=6
real(8) x(r_lim,c_lim)
real(8) x2(r_lim2,c_lim2)
do i=1,r_lim
do j=1,c_lim
x(i,j)= 10 * i + j
enddo
enddo
do i=1,r_lim2
do j=1,c_lim2
x2(i,j)= 100 * i + j
enddo
enddo
write(*,*) "----x(5,2)----"
call sub3(x,r_lim,c_lim)
write(*,*) "----x2(3,6)----"
call sub3(x2,r_lim2,c_lim2)
end program c120622
subroutine sub3(c,n,m)
real(8) c(n,m)
do i=1,n
do j=1,m
write(*,*) i,j,c(i,j)
enddo
enddo
end subroutine sub3
ーーーーーーーーーーーーーーーーーーーーーー
以下、実行結果です、
----x(5,2)----
1 1 11.000000000000000
1 2 12.000000000000000
2 1 21.000000000000000
2 2 22.000000000000000
3 1 31.000000000000000
3 2 32.000000000000000
4 1 41.000000000000000
4 2 42.000000000000000
5 1 51.000000000000000
5 2 52.000000000000000
----x2(3,6)----
1 1 101.00000000000000
1 2 102.00000000000000
1 3 103.00000000000000
1 4 104.00000000000000
1 5 105.00000000000000
1 6 106.00000000000000
2 1 201.00000000000000
2 2 202.00000000000000
2 3 203.00000000000000
2 4 204.00000000000000
2 5 205.00000000000000
2 6 206.00000000000000
3 1 301.00000000000000
3 2 302.00000000000000
3 3 303.00000000000000
3 4 304.00000000000000
3 5 305.00000000000000
3 6 306.00000000000000
fortranは、10年以上前のことなので、忘れていますが、たぶん、正しく処理できていると思います。
No.3
- 回答日時:
なんとなくですが、以下のような感じになるのでは・・・?
ただ、計算結果の戻し方がわかりません。
real A(10,20)
real B(20,30)
call sub(A, 10, 20)
call sub(B, 20, 30)
subroutine sub(C,n,m)
real C(n, m)
real kekka
do i=1,n
do j=1,m
kekka = kekka + C(i, j)
enddo
enddo
end subroutine sub
No.2
- 回答日時:
>A(i,j)を足し集める、というループ処理を書いたら、B(i,j)が引数の時には対応出来ません。
サブルーチン側では、Aが渡されれば、Aとして計算し、Bが渡されればBとして計算します。
>つまりカレンダーの並びが配列に収納されているとして、任意の月(これが配列A,Bに相当)、任意の日付(これが成分i,jに相当)の周4点の和を計算するようなサブルーチン(またはユーザー定義関数)です。
これを作ってみました。
get_waがサブルーチンですが、Aが渡されれば、Aとして計算し、Bが渡されればBとして計算します。
-----------------------------------------
#include <stdio.h>
double get_wa(double arrayX[12][31],int i,int j)
{
double sum = 0;
if ((i - 1) >= 0) sum += arrayX[i-1][j];
if ((i + 1) < 31) sum += arrayX[i+1][j];
if ((j - 1) >= 0) sum += arrayX[i][j-1];
if ((j + 1) < 31) sum += arrayX[i][j+1];
printf("get_wa内で印字:i=%d j=%d sum=%f\n",i,j,sum);
return sum;
}
main ()
{
int i,j;
double suma;
double sumb;
double arrayA[12][31];
double arrayB[12][31];
//配列に値を設定する
for(i = 0; i < 12;i++){
for(j = 0; j < 31;j++){
arrayA[i][j] = i*10 + j;
arrayB[i][j] = i*100 + j;
}
}
suma = get_wa(arrayA,2,3);
sumb = get_wa(arrayB,2,3);
printf("get_waの結果:suma=%f sumb=%f\n",suma,sumb);
suma = get_wa(arrayA,0,0);
sumb = get_wa(arrayB,0,0);
printf("get_waの結果:suma=%f sumb=%f\n",suma,sumb);
}
---------------------------------------------------
以下実行結果です。
get_wa内で印字:i=2 j=3 sum=92.000000
get_wa内で印字:i=2 j=3 sum=812.000000
get_waの結果:suma=92.000000 sumb=812.000000
get_wa内で印字:i=0 j=0 sum=11.000000
get_wa内で印字:i=0 j=0 sum=101.000000
get_waの結果:suma=11.000000 sumb=101.000000
ーーーーーーーーーーーーーーーーーーーーーーーー
arrayAとarrayBは、格納する値を変えています。
i,jの周囲の4点、つまり、以下の4点の和を計算しています。端っこは、計算しないようにしています。
i-1,j
i+1,j
i,j-1
i,j+1
-------------------------------------------
回答ありがとうございます。
アドレスなどの概念を使わずとも、配列を丸ごとサブルーチンに渡せるのでしょうか?
型とサイズは一致している必要がありそうですが・・・
書き忘れで申し訳ないのですが、実は配列A,Bはサイズが異なる可能性もあります。
ただ、サイズは明示的に定義しますので、サブルーチンの引数として配列サイズを渡すことは可能です。
このような場合にも、似たアルゴリズムで同じことの実現は可能でしょうか?
今、Cの動作環境が無くて確認できず、申し訳ありません。
ありがとうございました。
No.1
- 回答日時:
配列の型がすべて同一であるのであれば
関数のパラメータを
配列の先頭アドレス,i要素数,j要素数 とすれば いいだけの話だと思いますけど
(質問タイトルのまんまだけど)
Fortranは昔触ったことあるけどすっかり忘れ去ってるのでCで書きますが
雑なサンプル(未確認なのとネストわかりやすくするため 大文字・小文字・全角が入り乱れてます)
main()
{
double A[10][20];
double B[30][10];
sub(A,10,20);
sub(B,30,10);
}
sub(double *addr, int i, int j)
{
double Total=0.0;
for(int y=0; y<i; y ++)
{
for(int x=0; x<j; x ++)
{
//データの総計
Total =Total + *(addr+(y * x) + x) ;
//各配列要素までの総計データでバッファ内容を更新
*(addr+(y * x) + x) = Total ;
}
}
}
といったような形で扱えます
丁寧な回答ありがとうございました。
*(addr+(y * x) + x)という部分は、*()の中身がメモリにおける先頭アドレスからの距離でしょうか?*()を付与する事で、アドレスの中身を具体的に参照すると。
C言語では、A[0][0]~A[9][0]までが連続したアドレスに収納され、次のアドレスにA[0][1]が入っていると解釈すれば、そうなりそうです。
どこかで、Fortranはこの順番が、C言語と逆だと聞いたので、その場合はx,yを逆にすれば良いのでしょうか。
もう一つ、
sub(A,10,20);
の部分は
sub(&A,10,20);
でなくて構わないのでしょうか?
サブルーチン内でaddrはポインタとして宣言されているのだと思います。
ポインタにアドレスを渡すときには
addr=&A; のような記述をすると思うのですが、サブルーチンに引数を渡す時には、事情は違うのでしょうか?
また、回答待ちの間に少しFortranについて調べていたのですが、どうもFortran90/95では&や*に相当する演算子が無いみたいで、どう教えていただいたプログラムをFortranに移植しようか迷っているところです・・・
Fortranではあくまでポインタはある変数の「別名」として使えるだけみたいで、アドレスを直接的にいじる方法が、無さそうなのです。
つまり、Cでいうところの
double *ptr;
double data;
ptr=&data;
として、以後は*ptr(結局dataに収録された数字であって、dataの別名として*ptrを使う)を使えるだけで、ptr(アドレスの値)を参照する方法が、多分ですが、無い感じです。
もし何かお気づきの点ありましたらよろしくお願いします。
分かりにくい点、私の理解が足りない点、多々あると思います。申し訳ありません。
ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- PHP 配列の値の更新方法について 1 2022/08/05 09:49
- Perl perlで2次元配列をサブルーチンに値渡しで渡す 5 2022/12/17 18:49
- C言語・C++・C# 関数ポインタの高速化のメリット 7 2023/05/05 20:15
- C言語・C++・C# numpyスライス機能を使った数値計算 2 2023/05/08 16:01
- Java java 飾子を付けること(public static・・・) ・コンソールへの出力処理はmainメ 2 2022/06/16 19:34
- その他(プログラミング・Web制作) FORTRAN77の配列(除算) 2 2023/02/01 14:34
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- Java javaの質問です 次の機能を有するメソッド4つを自クラスに作成し、実装したいです 【機能】 足し算 1 2022/06/15 17:49
- Excel(エクセル) シートが違う2枚のエクセルシートにある数値を別シートにコピーしたい(VBA?) 8 2022/03/31 12:24
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
配列数式の解除
-
ListViewで、非表示列って作れ...
-
特定のセル範囲で4文字以上入力...
-
2つ以上の変数を比較して最大数...
-
VB6 配列を初期化したい
-
VBA 1次元配列を2次元に追加する
-
配列で飛び飛びの値を指定して...
-
VBA 1つの列を3つ以上の条件で...
-
Excel VBA配列をFunctionに渡す
-
配列変数の添字が範囲外ですと...
-
VBA Match関数の限界
-
OutlookVBAでサブフォルダ一括作成
-
Excel-VBAの配列「Public Const...
-
シェルスクリプト中で、ヒアド...
-
【VBA】配列とWorksheetFunctio...
-
for each の現在の配列ポインタ...
-
えfor文とか使っちゃう時点で時...
-
verilogで配列の任意の8bitを取...
-
コントロール配列のループ
-
2次元動的配列の第一引数のみを...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
配列数式の解除
-
2つ以上の変数を比較して最大数...
-
VBA 1次元配列を2次元に追加する
-
特定のセル範囲で4文字以上入力...
-
for each の現在の配列ポインタ...
-
VBのFunctionで、配列を引数...
-
subの配列引数をoptionalで使う...
-
VB6 配列を初期化したい
-
ListViewで、非表示列って作れ...
-
配列変数の添字が範囲外ですと...
-
Excel-VBAの配列「Public Const...
-
2次元動的配列の第一引数のみを...
-
VBAで近似曲線の係数取得
-
VLOOKUP関数で、一番下...
-
配列に同じ値を入れる方法
-
エクセルで最小値から0を除く方法
-
linest関数に配列を渡す
-
配列を任意の数値で埋める方法
-
Dim は何の略ですか?
-
配列内の内容を全て表示する方法
おすすめ情報
すいません、やはり例が単純過ぎましたので、もしサンプルプログラムを提供してくれる方は、以下の処理が行えるプログラムを書いて下さると嬉しいです。(やり方の概念のみの説明でも、構いません)
「配列A(i,j)、B(i,j)・・・がある時、任意の成分i,jの値の隣り合う4点の和を出力するようなサブルーチン」
つまりカレンダーの並びが配列に収納されているとして、任意の月(これが配列A,Bに相当)、任意の日付(これが成分i,jに相当)の周囲4点の和を計算するようなサブルーチン(またはユーザー定義関数)です。
端っこは周囲4点の計算が出来ないのでは?などということはとりあえず無視して構いません。よろしくお願いします。
Fortranでなくても、Cならギリギリ分かります。