2010-11-17 89 views
24

Tôi đã nhận thấy một điều kỳ lạ khi làm việc trong R. Khi tôi có một chương trình đơn giản tính toán các ô từ 1 đến N được thực hiện bằng cách sử dụng vòng lặp và lặp lại hành vi không phải là tương tự. (Tôi không quan tâm đến vectorisation trong trường hợp này hoặc áp dụng các chức năng).Vòng lặp for-loop vs while trong R

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 
    } 
} 

Kết quả là:

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 

Bây giờ chúng ta biết rằng cho vòng lặp là nhanh hơn, tôi đoán là do phân bổ trước và optimisations đó. Nhưng tại sao nó tràn?

UPDATE: Bây giờ cố gắng một cách khác với vectơ:

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 

Vì vậy, Có lẽ một vấn đề bộ nhớ sôi nổi của nó? Tôi đang chạy trên OS X với 4Gb bộ nhớ và tất cả các thiết lập mặc định trong R. Điều này xảy ra trong phiên bản 32 và 64 bit (ngoại trừ thời gian đó nhanh hơn).

Alex

+6

Dựa trên thời gian trong khi vòng lặp của bạn nhanh hơn. – Marek

+2

khi bạn chuyển đổi bộ đếm trong vòng lặp for thành float, nó sẽ nhanh hơn vòng lặp while nhưng nó chỉ vì vòng lặp for sau đó không có cảnh báo. – John

+1

R là đầy đủ các loại vô nghĩa này. Tuy nhiên, –

Trả lời

36

1 là số, nhưng không phải là số nguyên (ví dụ đó là một số dấu chấm động), và 1:6000 là số và số nguyên.

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

60000 bình là 3,6 tỷ đồng, mà không phải là biểu diễn trong ký 32-bit số nguyên, do đó bạn nhận được một lỗi tràn bộ nhớ:

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

3,6 tỷ đồng là dễ dàng biểu diễn trong dấu chấm động, tuy nhiên:

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

để sửa mã for của bạn, chuyển đổi sang một đại diện dấu chấm động:

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

hoặc 'cho (i trong seq (1, N, 1))' ... –

+2

là tại sao seq (N) được ưu tiên hơn 1: N? –

+0

@Joris hoặc 'seq_len (N)' – Marek

4

Biến trong vòng lặp for là một chuỗi số nguyên, và vì vậy cuối cùng bạn làm điều này:

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

trong khi trong vòng lặp trong khi bạn đang tạo ra một số dấu chấm động.

của nó cũng là lý do những điều này là khác nhau:

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

Đừng tin tôi?

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

vì:

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

Nhưng tại sao các điểm trôi nổi có phạm vi lớn hơn số nguyên? – Alex

+1

CẬP NHẬT: "Lưu ý rằng trên hầu hết các lần triển khai R phạm vi các số nguyên có thể biểu diễn được giới hạn khoảng +/- 2 * 10^9: đôi có thể giữ nguyên số nguyên lớn hơn nhiều." Từ tài liệu R cho số nguyên: ( – Alex

+1

Vì đó là điểm nổi là FOR. –

3

Và khoảng thời gian:

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 

Và bây giờ chúng ta biết rằng cho vòng lặp là nhanh hơn so với vòng lặp while. Bạn không thể bỏ qua cảnh báo trong thời gian.

+1

Điều này vẫn chưa hoàn toàn công bằng vì vòng lặp có thân hình lớn hơn; điều này là cần thiết để thi đua, tôi biết, nhưng trong một số vấn đề này không phải là trường hợp. – mbq

+2

@mbq Đó là lý do tại sao vòng lặp và vòng lặp while không thể so sánh được. Mỗi mục đích khác nhau. Bạn có thể thêm dòng 'i <-i + 1' vào' fn1' nhưng vẫn nhanh hơn vì 'fn2' phải kiểm tra điều kiện, có nghĩa là 60k gọi tới' <= '. Nếu bạn thêm một dòng 'i <= N' vào' fn1' thì thời gian bằng nhau. – Marek

Các vấn đề liên quan