web-dev-qa-db-fra.com

Boucle For vs boucle While en R

J'ai remarqué une chose curieuse en travaillant dans R. Quand j'ai un programme simple qui calcule les carrés de 1 à N implémenté en utilisant for-loop et while-loop, le comportement n'est pas le même. (Je ne me soucie pas de la vectorisation dans ce cas ou d'appliquer des fonctions).

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

ET

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

Les résultats sont:

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 

Maintenant, nous savons que la boucle for est plus rapide, je suppose que c'est à cause de la pré-allocation et des optimisations. Mais pourquoi déborde-t-il?

MISE À JOUR: Alors maintenant, essayons d'une autre manière avec des vecteurs:

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

Alors peut-être que c'est un problème de mémoire génial? Je fonctionne sous OS X avec 4 Go de mémoire et tous les paramètres par défaut dans R. Cela se produit dans les versions 32 et 64 bits (sauf que les temps sont plus rapides).

Alex

25
Alex

Car 1 est numérique, mais pas entier (c'est-à-dire qu'il s'agit d'un nombre à virgule flottante), et 1:6000 est numérique et entier.

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

60000 au carré est de 3,6 milliards, ce qui n'est PAS représentable en entier 32 bits signé, d'où vous obtenez une erreur de débordement:

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

3,6 milliards sont facilement représentables en virgule flottante, cependant:

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

Pour corriger votre code for, convertissez-le en une représentation à virgule flottante:

function (N)
{
    for(i in as.single(1:N)) {
        y <- i*i
    }
}
37
Alex Brown

La variable dans la boucle for est une séquence entière, et donc finalement vous faites ceci:

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

tandis que dans la boucle while, vous créez un nombre à virgule flottante.

C'est aussi la raison pour laquelle ces choses sont différentes:

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

Tu ne me crois pas?

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

car:

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

Et sur le 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

Et maintenant, nous savons que la boucle for est plus rapide que la boucle while. Vous ne pouvez pas ignorer les avertissements pendant le chronométrage.

3
Marek