Цикл For против цикла while в R - PullRequest
25 голосов
/ 17 ноября 2010

Я заметил любопытную вещь во время работы в R. Когда у меня есть простая программа, которая вычисляет квадраты от 1 до N, реализованные с использованием for-loop и while-loop, поведение не одинаково.(Меня не волнует векторизация в этом случае или применение функций).

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

AND

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 с 4 ГБ памяти и всеми настройками по умолчанию в R. Это происходит в 32- и 64-разрядных версиях (за исключением того, что время быстрее).

Alex

Ответы [ 3 ]

37 голосов
/ 17 ноября 2010

Поскольку 1 является числовым, но не целым числом (т. Е. Это число с плавающей запятой), а 1:6000 является числовым и целым числом.

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

60000 в квадрате составляет 3,6 млрд., Что НЕ представляетсяв 32-битном целом со знаком, следовательно, вы получаете ошибку переполнения:

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

3,6 миллиарда легко представимы в плавающей запятой, однако:

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

Чтобы исправить код for, преобразовать в представление с плавающей запятой:

function (N)
{
    for(i in as.single(1:N)) {
        y <- i*i
    }
}
4 голосов
/ 17 ноября 2010

Переменная в цикле 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
3 голосов
/ 17 ноября 2010

А по поводу времени:

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.Вы не можете игнорировать предупреждения во время.

...