2010-11-17 18 views
24

He notado algo curioso mientras trabajo en R. Cuando tengo un programa simple que calcula cuadrados de 1 a N implementados usando for-loop y while-loop el comportamiento no es el mismo. (No me importa la vectorización en este caso o aplicar funciones).For-loop vs while loop en R

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

Y

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

Los resultados son los siguientes:

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 

Ahora sabemos que para los de ciclo es más rápido, yo creo que es debido a la asignación previa y optimizaciones allí. Pero, ¿por qué se desborda?

ACTUALIZACIÓN: Así que ahora tratar de otra manera con vectores:

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 

así que quizás es un problema de memoria cobarde? Me estoy ejecutando en OS X con 4 GB de memoria y todos los ajustes predeterminados en R. Esto sucede en las versiones de 32 y 64 bits (excepto que los tiempos son más rápidos).

Alex

+6

Según el tiempo while-loop es más rápido. – Marek

+2

cuando convierte el contador en el bucle for a un float, será más rápido que el bucle while, pero solo porque el bucle for no tiene advertencias. – John

+1

R está lleno de este tipo de tonterías. –

Respuesta

36

Debido 1 es numérico, pero no entero (es decir, que es un número de coma flotante), y 1:6000 es numérico y entero.

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

60000 cuadrado es de 3,6 mil millones, lo cual no es representable en el entero de 32 bits, por lo tanto, se obtiene un error de desbordamiento:

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

3,6 mil millones es fácilmente representable en coma flotante, sin embargo:

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

para fijar su código for, convertir a una representación de punto flotante:

function (N) 
{ 
    for(i in as.single(1:N)) { 
     y <- i*i 
    } 
} 
+3

o 'para (i en la SEC (1, N, 1))' ... –

+2

¿es por eso SEC (N) es preferible a 1: N –

+0

@Joris o 'seq_len (N)' – Marek

4

La variable en el bucle for es una secuencia de enteros, y por lo que finalmente se hace esto:

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

mientras que en el bucle mientras se está creando un número de punto flotante.

También es la razón de que estas cosas son diferentes:

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

no me creen?

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

porque:

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

Pero, ¿por qué los puntos flotantes tienen un rango mayor que los enteros? – Alex

+1

ACTUALIZACIÓN: "Tenga en cuenta que en casi todas las implementaciones de R el rango de números enteros representables se limita a aproximadamente +/- 2 * 10^9: dobles pueden contener números enteros mucho más grandes con exactitud." A partir de la documentación R por entero :( – Alex

+1

Porque eso es lo que está en coma flotante PARA –

3

Y sobre el calendario:

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 

Y ahora sabemos que para los de ciclo es más rápido que el bucle while. No puede ignorar las advertencias durante el tiempo.

+1

Esto todavía no es completamente justo ya que mientras el lazo tiene un cuerpo más grande; Esto es necesario para emular, lo sé, pero en algunos problemas este no es el caso. – mbq

+2

@mbq Por eso for-loop y while-loop no se pueden comparar. Cada uno tiene un propósito diferente. Puede agregar la línea 'i <-i + 1' en' fn1' pero aún así será más rápido porque 'fn2' tiene que verificar la condición, lo que significa 60k llamadas a' <= '. Si agrega otra línea 'i <= N' a' fn1', los tiempos son iguales. – Marek