2010-11-17 22 views
24

Ho notato una cosa curiosa mentre lavoravo in R. Quando ho un semplice programma che calcola i quadrati da 1 a N implementati usando for-loop e while-loop il comportamento non è il stesso. (Non mi interessa la vettorizzazione in questo caso o applicare le funzioni).Per ciclo continuo mentre ciclo in R

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

E

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

I risultati sono:

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 

Ora sappiamo che per-ciclo è più veloce, la mia ipotesi è a causa della pre assegnazione e ottimizzazioni lì. Ma perché trabocca?

UPDATE: Così ora cercando un altro modo con i vettori:

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 

così forse il suo un problema di memoria funky? Sono in esecuzione su OS X con 4 GB di memoria e tutte le impostazioni predefinite in R. Ciò accade nelle versioni a 32 e 64 bit (tranne che le volte sono più veloci).

Alex

+6

In base al tempo durante il quale il ciclo è più veloce. – Marek

+2

quando convertite il contatore del ciclo for in un float, sarà più veloce del ciclo while, ma è solo perché il ciclo for non ha avvertimenti. – John

+1

R è pieno di questo genere di assurdità. –

risposta

36

Poiché 1 è numerico, ma non intero (cioè esso è un numero decimale), e 1:6000 è numerica e intero.

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

60000 Squared è 3,6 miliardi di euro, che non è rappresentabile in intero con segno a 32 bit, quindi si ottiene un errore di overflow:

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

3,6 miliardi è facilmente rappresentabili in virgola mobile, però:

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

per risolvere il tuo codice di for, convertire in una rappresentazione in virgola mobile:

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

o 'for (i in seq (1, N, 1))' ... –

+2

è questo il motivo per cui (N) è preferibile a 1: N? –

+0

@Joris o 'seq_len (N)' – Marek

4

La variabile nel ciclo for è una sequenza di numeri interi, e così alla fine si esegue questa operazione:

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

mentre nel ciclo while si crea un numero decimale.

E 'anche il motivo per queste cose sono diverse:

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

Non mi credete?

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

perché:

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

Ma perché i punti mobili hanno un intervallo più ampio degli interi? – Alex

+1

UPDATE: "Notare che su quasi tutte le implementazioni di R l'intervallo di numeri interi rappresentabili è limitato a circa +/- 2 * 10^9: i doppi possono contenere esattamente interi molto più grandi." Dalla documentazione R per intero :( – Alex

+1

Perché questo è il punto in virgola mobile FOR. –

3

E una questione di tempi:

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 

E ora sappiamo che per-ciclo è più veloce di ciclo while. Non puoi ignorare gli avvisi durante i tempi.

+1

Questo non è ancora del tutto corretto poiché il ciclo ha un corpo più grande; questo è necessario per emulare, lo so, ma in alcuni problemi questo non è il caso. – mbq

+2

@mbq Ecco perché il ciclo e il ciclo non possono essere confrontati. Ognuno ha uno scopo diverso. Puoi aggiungere la riga 'i <-i + 1' in' fn1', ma è ancora più veloce perché 'fn2' deve controllare la condizione, il che significa chiamate di 60k a' <= '. Se aggiungete un'altra riga 'i <= N' a' fn1' allora i tempi sono uguali. – Marek

Problemi correlati