web-dev-qa-db-ja.com

Rのforループとwhileループ

Rでの作業中に不思議なことに気付きました。forループとwhileループを使用して実装された1からNまでの平方を計算する簡単なプログラムがある場合、動作は同じではありません。 (この場合、ベクトル化は気にしませんし、関数を適用しません)。

fn1 <- function (N) 
{
    for(i in 1:N) {
        y <- i*i
    }
}

そして

fn2 <- function (N) 
{
    i=1
    while(i <= N) {
        y <- i*i
        i <- i + 1
    }
}

結果は次のとおりです。

system.time(fn1(60000))
   user  system elapsed 
  2.500   0.012   2.493 
There were 50 or more warnings (use warnings() to see the first 50)
Warning messages:
1: In i * i : NAs produced by integer overflow
.
.
.

system.time(fn2(60000))
   user  system elapsed 
  0.138   0.000   0.137 

これで、forループの方が高速であることがわかりました。私の推測は、事前割り当てと最適化によるものです。しかし、なぜオーバーフローするのでしょうか?

更新:それで、ベクターで別の方法を試してみましょう:

fn3 <- function (N) 
{
    i <- 1:N
    y <- i*i
}
system.time(fn3(60000))
   user  system elapsed 
  0.008   0.000   0.009 
Warning message:
In i * i : NAs produced by integer overflow

おそらく、ファンキーなメモリの問題でしょうか? 4 GbのメモリとRのすべてのデフォルト設定でOS Xを実行しています。これは、32ビットおよび64ビットバージョンで発生します(ただし、時間が速いことを除く)。

アレックス

25
Alex

なぜなら1は数値ですが、整数ではなく(つまり、浮動小数点数です)、1:6000は数値および整数です。

> print(class(1))
[1] "numeric"
> print(class(1:60000))
[1] "integer"

60000の2乗は36億です。これは符号付き32ビット整数では表現できないため、オーバーフローエラーが発生します。

> as.integer(60000)*as.integer(60000)
[1] NA
Warning message:
In as.integer(60000) * as.integer(60000) : NAs produced by integer overflow

ただし、36億は浮動小数点で簡単に表現できます。

> as.single(60000)*as.single(60000)
[1] 3.6e+09

forコードを修正するには、浮動小数点表現に変換します。

function (N)
{
    for(i in as.single(1:N)) {
        y <- i*i
    }
}
37
Alex Brown

Forループ内の変数は整数シーケンスであるため、最終的にこれを行います。

> y=as.integer(60000)*as.integer(60000)
Warning message:
In as.integer(60000) * as.integer(60000) : NAs produced by integer overflow

一方、whileループでは、浮動小数点数を作成しています。

また、これらのことが異なる理由もあります。

> seq(0,2,1)
[1] 0 1 2
> seq(0,2)
[1] 0 1 2

信じられない?

> identical(seq(0,2),seq(0,2,1))
[1] FALSE

なぜなら:

> is.integer(seq(0,2))
[1] TRUE
> is.integer(seq(0,2,1))
[1] FALSE
4
Spacedman

そしてタイミングについて:

fn1 <- function (N) {
    for(i in as.numeric(1:N)) { y <- i*i }
}
fn2 <- function (N) {
    i=1
    while (i <= N) {
        y <- i*i
        i <- i + 1
    }
}

system.time(fn1(60000))
# user  system elapsed 
# 0.06    0.00    0.07 
system.time(fn2(60000))
# user  system elapsed 
# 0.12    0.00    0.13

そして今、for-loopはwhile-loopよりも速いことがわかりました。タイミング中に警告を無視することはできません。

3
Marek