2010-11-17 11 views
24

Zauważyłem ciekawą rzecz podczas pracy w R. Kiedy mam prosty program, który oblicza kwadraty od 1 do N zaimplementowane za pomocą pętli for i pętli while, zachowanie nie jest podobnie. (Nie interesuje mnie wektoryzacja w tym przypadku lub zastosowanie funkcji).For-loop vs while loop w R

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

I

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

Wyniki są następujące:

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 

Teraz wiemy, że pętla jest szybsza, domyślam się, ponieważ wstępnego przydziału i optymalizacji tam. Ale dlaczego to się przelewa?

UPDATE: Więc teraz próbuje w inny sposób z wektorami:

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 

więc może jego funky problem pamięci? Używam systemu OS X z 4 GB pamięci i wszystkimi domyślnymi ustawieniami w R. Zdarza się to w wersjach 32- i 64-bitowych (poza tym, że czasy są szybsze).

Alex

+6

Na podstawie czasu wykonywania pętli while jest szybszy. – Marek

+2

po konwersji licznika w pętli for na float będzie on szybszy niż pętla while, ale dzieje się tak tylko dlatego, że pętla for nie ma wtedy żadnych ostrzeżeń. – John

+1

R jest pełen tego rodzaju nonsensów. –

Odpowiedz

36

Ponieważ 1 jest liczbą, ale nie całkowitą (to znaczy, że jest liczbą rzeczywistą), a 1:6000 jest liczbą całkowitą i.

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

60000 kwadratu wynosi 3,6 miliarda dolarów, co nie jest do zakodowania w podpisanym 32-bitowej liczby całkowitej, a więc pojawi się błąd przepełnienia:

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

3,6 miliard łatwo reprezentowalna w zmiennoprzecinkowych, jednakże:

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

naprawić swój kod for, konwersja do reprezentacji zmiennoprzecinkowej:

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

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

+2

jest to dlaczego seq (N) jest korzystniejsze niż 1: N? –

+0

@Joris lub 'seq_len (N)' – Marek

4

zmiennej w pętli for jest sekwencją całkowitą, a więc w końcu to zrobić:

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

natomiast w pętli podczas tworzenia liczbę zmiennoprzecinkową.

Jest też powodu te rzeczy są różne:

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

Nie wierzysz mi?

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

ponieważ:

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

Ale dlaczego zmienne mają większy zasięg niż liczby całkowite? – Alex

+1

UPDATE: "Zauważ, że w prawie wszystkich implementacjach R zakres reprezentowalnych liczb całkowitych jest ograniczony do około +/- 2 * 10^9: duble mogą zawierać dokładnie więcej liczb całkowitych." Z dokumentacji R dla liczby całkowitej :( – Alex

+1

Bo to jest zmiennoprzecinkowe dla: –

3

I o czas:

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 

A teraz wiemy, że dla pętli while jest szybsza niż pętli. Nie można ignorować ostrzeżeń w czasie.

+1

To nadal nie jest w pełni fair, ponieważ pętla while ma większe ciało; jest to konieczne do naśladowania, wiem, ale w niektórych problemach tak nie jest. – mbq

+2

@mbq To dlatego nie można porównywać pętli for i loop. Każdy ma inny cel. Możesz dodać linię 'i <-i + 1' w' fn1', ale nadal będzie szybciej, ponieważ 'fn2' musi sprawdzić warunek, co oznacza, że ​​60k wywoła' <= '. Jeśli dodasz inny wiersz 'i <= N' do' fn1', wtedy czasy będą równe. – Marek