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?
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
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.
all(diff(x)<0)
(substituez >
, <=
, >=
selon le cas)
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
.
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