2010-11-17 4 views
24

Ich habe eine seltsame Sache bemerkt, während der Arbeit in R. Wenn ich ein einfaches Programm, das Quadrate von 1 bis N implementiert mit For-Schleife und While-Schleife das Verhalten ist nicht die gleich. (In diesem Fall ist mir die Vektorisierung egal, oder Funktionen anwenden).For-Schleife vs While-Schleife in R

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

UND

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

Die Ergebnisse sind:

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 

Jetzt wissen wir, dass für-Schleife schneller, meine Vermutung wegen pre Zuteilung ist und dort Optimierungen. Aber warum ist es übergelaufen?

UPDATE: Versuch also nun eine andere Art und Weise mit Vektoren:

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 

So sein Vielleicht ein flippiges Speicherproblem? Ich laufe auf OS X mit 4 GB Speicher und allen Standardeinstellungen in R. Dies geschieht in 32- und 64-Bit-Versionen (außer dass die Zeiten sind schneller).

Alex

+6

Basierend auf Ihrem Timing While-Loop ist schneller. – Marek

+2

Wenn Sie den Zähler in der for-Schleife in einen Gleitkomma konvertieren, wird es schneller als die while-Schleife, aber nur, weil die for-Schleife dann keine Warnungen hat. – John

+1

R ist voll von dieser Art von Unsinn. –

Antwort

36

Da 1 numerisch ist, aber nicht integer (das heißt, es ist eine Gleitkommazahl) und 1:6000 numerisch ist und integer.

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

60000 Quadrat ist 3,6 Milliarden, die in ganzzahligen 32-Bit nicht darstellbar ist signiert, damit Sie einen Überlauffehler erhalten:

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

3,6 Mrd. in Gleitkomma leicht darstellbare, aber:

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

um Ihre for Code zu beheben, konvertieren zu einer Gleitkommadarstellung:

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

oder 'für (i in seq (1, N, 1))' ... –

+2

ist das, warum seq (N) ist gegenüber 1 bevorzugt: N? –

+0

@Joris oder 'seq_len (N)' – Marek

4

die Variable in der for-Schleife ist eine Ganzzahl-Sequenz, und so schließlich Sie tun dies:

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

während in der while-Schleife Sie eine Gleitkommazahl schaffen.

Sein Grund auch diese Dinge sind anders:

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

Sie glauben mir nicht?

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

weil:

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

Aber warum haben Fließkomma-Punkte einen größeren Bereich als ganze Zahlen? – Alex

+1

UPDATE: "Beachten Sie, dass bei fast allen Implementierungen von R der Bereich der darstellbaren Ganzzahlen auf etwa +/- 2 * 10^9 beschränkt ist: Doubles können viel größere ganze Zahlen genau enthalten." From R Dokumentation für Integer :( – Alex

+1

Weil das ist, was Fließkomma für FOR ist. –

3

Und Timing:

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 

Und jetzt wissen wir, dass for-Schleife ist schneller als while-Schleife. Sie können Warnungen nicht während des Timings ignorieren.

+1

Dies ist immer noch nicht ganz fair seit while-Schleife haben größere Körper; das ist notwendig, um zu emulieren, weiß ich, aber in einigen Problemen ist dies nicht der Fall. – mbq

+2

@mbq Deshalb warum For-Schleife und While-Schleife kann nicht verglichen werden. Jeder hat einen anderen Zweck. Sie können 'i <-i + 1'-Zeile in' fn1' hinzufügen, aber es ist immer noch schneller, weil 'fn2' die Bedingung prüfen muss, was 60k-Aufrufe von' <= 'bedeutet. Wenn Sie eine weitere Zeile "i <= N" zu "fn1" hinzufügen, sind die Timings gleich. – Marek