web-dev-qa-db-fra.com

Comment vérifier si une suite de nombres augmente (ou diminue) de façon monotone?

On nous donne une séquence de nombres, en tant que vecteur foo. La tâche consiste à déterminer si foo est augmente de manière monotone - chaque élément est inférieur ou égal au suivant - ou diminue de manière monotone - chaque élément est supérieur ou égal au suivant. 

Bien sûr, cela peut être trouvé à travers une boucle, mais des idées plus créatives?

37
Ali

Un autre: vérifier si

all(x == cummax(x))

ou

all(x == cummin(x))

pour augmenter ou diminuer monotone respectivement. Il semble que cummax est beaucoup plus rapide que diff et utilise également moins de mémoire:

> x <- seq_len(1e7)
> system.time(all(x == cummax(x)))
   user  system elapsed 
   0.11    0.00    0.11 
> system.time(all(diff(x) >= 0))
   user  system elapsed 
   0.47    0.13    0.59

> x <- seq_len(1e8)
> system.time(all(x == cummax(x)))
   user  system elapsed 
   1.06    0.09    1.16 
> system.time(all(diff(x) >= 0))
Error: cannot allocate vector of size 381.5 Mb
In addition: Warning messages:
1: Reached total allocation of 1535Mb: see help(memory.size) 
2: Reached total allocation of 1535Mb: see help(memory.size) 
3: Reached total allocation of 1535Mb: see help(memory.size) 
4: Reached total allocation of 1535Mb: see help(memory.size) 
Timing stopped at: 1.96 0.38 2.33

Mon pari de savoir pourquoi cummax est plus rapide que diff est qu’il n’a qu’à comparer les nombres, ce qui est plus rapide que de devoir calculer des différences.

Edit : à la demande de votre (ALi), tests supplémentaires, y compris votre réponse (notez que je travaille maintenant à partir d'une machine différente, les résultats suivants ne doivent donc pas être comparés à ceux mentionnés ci-dessus)

> x <- seq_len(1e7)
> system.time(x == cummax(x))
   user  system elapsed 
  0.316   0.096   0.416 
> system.time(all(diff(x) >= 0))
   user  system elapsed 
  4.364   0.240   4.632 
> system.time(x[-1] - x[-length(x)] >= 0)
   user  system elapsed 
  3.828   0.380   4.227
> system.time(all(x[-1] >= x[-length(x)]))
   user  system elapsed 
  2.572   0.288   2.865 
51
flodel

Une option consiste à utiliser la fonction diff() pour donner les différences entre les éléments adjacents dans un vecteur.

Une fonction monotone croissante aura diff(x) all> ou égal à 0:

f1 <- 1:10
f2 <- 10:1

> all(diff(f1) >= 0)
[1] TRUE
> all(diff(f2) >= 0)
[1] FALSE

Bien que le test d'égalité avec 0 puisse être mal vu; Mieux vaut peut-être utiliser < 0 et annuler la comparaison via !:

> all(!diff(f1) < 0)
[1] TRUE
> all(!diff(f2) < 0)
[1] FALSE

La raison en est que vous utilisez un ordinateur sur lequel tous les nombres ne peuvent pas être représentés exactement. Vous pouvez calculer un résultat qui est effectivement nul mais pas tout à fait zéro car les nombres dans le calcul ne pourraient pas être représentés exactement (c'est-à-dire points flottants ). Donc, si foo est le résultat d'un calcul, tester si elle est égale à 0 pourrait donner comme résultat que 0 devrait être un tout petit supérieur ou inférieur à 0, ce qui pourrait donner un résultat erroné pour une fonction croissante/décroissante.

14
Gavin Simpson

all(diff(x)<0) (substituez >, <=, >= selon le cas)

7
Ben Bolker

Pour la version croissante, vous pouvez utiliser is.unsorted():

x <- seq_len(1e7)
!is.unsorted(x)

> !is.unsorted(x)
[1] TRUE

C'est assez rapide aussi:

> system.time(!is.unsorted(x))
   user  system elapsed 
  0.099   0.000   0.099 
> system.time(all(x == cummax(x)))
   user  system elapsed 
  0.320   0.039   0.360

Malheureusement, is.unsorted() est explicitement pour l'ordre croissant. Nous convertissons cela en une situation décroissante, mais cela reste compétitif par rapport aux autres options de mon système:

xx <- 1e7:1
!is.unsorted(-xx)
system.time(!is.unsorted(-xx))

> system.time(!is.unsorted(-xx))
   user  system elapsed 
  0.205   0.020   0.226 
> system.time(all(xx == cummin(xx)))
   user  system elapsed 
  0.356   0.088   0.444

Et sur un plus gros problème ...

x  <- 1:1e8
xx <- 1e8:1
system.time(!is.unsorted(x))
system.time(all(x == cummax(x)))
system.time(!is.unsorted(-xx))
system.time(all(xx == cummin(xx)))

> system.time(!is.unsorted(x))
   user  system elapsed 
  1.019   0.000   1.019 
> system.time(all(x == cummax(x)))
   user  system elapsed 
  3.255   0.354   3.608 
> system.time(!is.unsorted(-xx))
   user  system elapsed 
  2.089   0.561   2.650 
> system.time(all(xx == cummin(xx)))
   user  system elapsed 
  3.318   0.395   3.713

Si vous voulez forcer une séquence strictement croissante, voyez alors strictly dans ?is.unsorted.

4
Gavin Simpson

Une réponse intéressante est comme ci-dessous:

foo = c(1, 3, 7, 10, 15)
all(foo[-1] - foo[-length(foo)] >= 0) # TRUE

foo[3] = 20
all(foo[-1] - foo[-length(foo)] >= 0) # FALSE
0
Ali