No.07522 forを使わないようにするために  【波音】 2008/08/30(Sat) 20:09

Rのプログラミングにおいて,繰り返し構文(for構文)はなるべく使わないようにするのが望ましいといわれていますが,どうしても私はfor構文を使って繰り返しをすることに頼ってしまいます(^_^;)

例えば,「縦の長さをa,横の長さをb,面積をSとする(S=a*b)。このとき,S=150となるaとbの組み合わせをすべてあげよ。ただし,aもbも整数であるとする。」というような問題を考えるとき,たぶん,もっとも典型的には

myprog <- function(){
for(a in 1:150){
for(b in 150:1){
num <- 150 / b
if(num==a) cat("a = ", a, ", ", "b = ", b, "\n")
}
}
}

とかけると思います。

> myprog()
a = 1 , b = 150
a = 2 , b = 75
a = 3 , b = 50
a = 5 , b = 30
a = 6 , b = 25
a = 10 , b = 15
a = 15 , b = 10
a = 25 , b = 6
a = 30 , b = 5
a = 50 , b = 3
a = 75 , b = 2
a = 150 , b = 1

これをRの関数を上手く使って,もっと簡単に(適切に?)書くためにはどういう書き方ができるのでしょうか?

こういう場合,いくつも方法があるのでしょうか,それとも「大体の場合はこうするんだよ」という定石みたいなやり方があるのでしょうか。。。

No.07523 Re: forを使わないようにするために  【青木繁伸】 2008/08/30(Sat) 20:59

私も,for を使って書く方がいいと思いますし,実際にそうします。
多くの場合,for で繰り返される内容は複雑(数行で書かれるものではない)なので,sapply などで書くのはかえって面倒(関数部分を無名関数で書こうとすると,結構長くなるので,結局は独立して関数定義を書いたりすることに。そうする と,sapply は for の単なる形式的な置き換えになる。実際,そのようにしても実行速度は殆ど同じ。
ただし,アルゴリズムの違いは大きな違いを産むことも。例の場合には,探索は二重ループでやる必要はない。
myprog <- function(){
for(a in 1:150){
for(b in 150:1){
num <- 150 / b
if(num==a) cat("a = ", a, ", ", "b = ", b, "\n")
}
}
}
system.time(myprog())
myprog2 <- function() {
for (a in 1:150) {
if (floor(150/a) == 150/a) cat("a = ", a, ", b = ", 150/a, "\n")
}
}
system.time(myprog2())
system.time(sapply(1:150, function(a) {ans <- 150/a; if (floor(ans) == ans) cat("a = ", a, ", b = ", ans, "\n")}))
一番目の例は二番目,三番目の例の5,60倍の時間が掛かるかも。二番目,三番目は殆ど差はないはず。
まあ,それにしてもいずれも実行時間は一秒未満,ああだこうだ工夫する必要もない。どうも,Rの開発者陣は,些細なことに注力するようで。一昔前のソフトウエア分野の指導者は,そんなことは「無駄なこと」と喝破していたのだけど(参考;「プログラミング作法」など)。

No.07529 Re: forを使わないようにするために  【波音】 2008/08/31(Sun) 09:54

回答ありがとうございます。

実際に3つのプログラムの実行速度を確かめてみると,2重ループを使ったヤツは時間がかかるようです(とはいっても,私たちが遅い速いと認識できるレベルでの違いはないようですが)。

forを使って書く場合は,150/aのベクトルと,floor(150/a)のベクトルを比べるようにしてやればよかったのですね。floor()を使うことは思いつくことができませんでした。

で も実際問題,sapplyで書き換えても実行速度がそれほど変わらないのであれば,forをsapplyで書き換える手間もいらないように思えてしまいま すね。まして,私のようにそんなに大きなプログラムを組んで動かさない人間にとっては,どちらでも"同じ"としか・・・

No.07530 Re: forを使わないようにするために  【青木繁伸】 2008/08/31(Sun) 13:11

> forを使って書く場合は,150/aのベクトルと,floor(150/a)のベクトルを比べるようにしてやればよかったのですね。

おっと,そうでしたね。ベクトル演算を活用するなら
> a <- 150/1:150
> print(a[a == floor(a)])
[1] 150 75 50 30 25 15 10 6 5 3 2 1
とすべきでした。

● 「統計学関連なんでもあり」の過去ログ--- 042 の目次へジャンプ
● 「統計学関連なんでもあり」の目次へジャンプ
● 直前のページへ戻る