R中的For-loop vs while循环

时间:2010-11-17 10:26:15

标签: r for-loop floating-point while-loop

我在R工作时注意到了一件好奇的事。 当我有一个简单的程序来计算使用for-loop和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

那也许它是一个时髦的记忆问题?我在OS X上运行4G内存和R中的所有默认设置。这种情况发生在32位和64位版本中(除了时间更快)。

亚历

3 个答案:

答案 0 :(得分:37)

因为1是数字,而不是整数(即它是浮点数),1:6000是数字和整数。

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

60000平方为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
    }
}

答案 1 :(得分:4)

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

答案 2 :(得分:3)

关于时间安排:

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循环比while循环更快。您不能在计时期间忽略警告。