For-loop vs while loop in R
Я заметил любопытную вещь, работая в Р.
Когда у меня есть простая программа, которая вычисляет квадраты от 1 до N, реализованные с использованием for-loop и while-loop, это не одно и то же. (В этом случае мне не нужны векторизация или применять функции).
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-loop быстрее, я думаю, из-за предварительного выделения и оптимизации там. Но почему он переполняется?
UPDATE: теперь попробуйте другой способ с векторами:
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-разрядных версиях (за исключением того, что время быстрее).
Алекс
Ответы
Ответ 1
Потому что 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
}
}
Ответ 2
Переменная в цикле 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
И о сроках:
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. Вы не можете игнорировать предупреждения во время синхронизации.