【问题标题】:How to check if a sequence of numbers is monotonically increasing (or decreasing)?如何检查一个数字序列是否单调递增(或递减)?
【发布时间】:2012-10-17 03:09:19
【问题描述】:

我们得到一个数字序列,作为向量foo。任务是找到foo 单调递增 - 每一项小于或等于下一项 - 或单调递减 - 每一项大于或等于下一个。

确定这可以通过循环找到,但还有更多创意吗?

【问题讨论】:

    标签: r vector


    【解决方案1】:

    all(diff(x)<0)(酌情替换><=>=

    【讨论】:

      【解决方案2】:

      一个有趣的答案如下:

      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
      

      【讨论】:

      • foo[-1] - foo[-length(foo) 实际上只是 diff(foo) 的简写(所以这变成了这里给出的其他答案)......但它更难阅读。
      【解决方案3】:

      一种选择是使用diff() 函数来给出向量中相邻元素之间的差异。

      单调递增函数将有diff(x) all > 或等于0:

      f1 <- 1:10
      f2 <- 10:1
      
      > all(diff(f1) >= 0)
      [1] TRUE
      > all(diff(f2) >= 0)
      [1] FALSE
      

      虽然测试与0 的相等性可能会令人不悦;最好使用&lt; 0 并通过! 否定比较:

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

      原因是您使用的计算机无法准确表示所有数字。您可能计算出一个实际上为零但不完全为零的结果,因为计算中的数字无法准确表示(即floating points)。因此,如果foo 是计算的结果,则测试它是否等于 0 可能会导致应该是 0 稍微大于或小于 0 的结果,这可能会为增加/减少函数给出错误的结果。

      【讨论】:

      • 这取决于你如何计算foo。请记住,您使用的计算机并非所有数字都可以准确表示。您可能计算出一个实际上为零但不完全为零的结果,因为计算中的数字无法准确表示。因此,如果foo 是计算的结果,则测试它是否等于 0 可能会导致应该是 0 稍微大于或小于 0 的结果,这可能会为增加/减少函数给出错误的结果。
      • 太棒了!为什么不将此评论作为您答案的一部分?
      • 话虽如此,舍入问题可能会导致您使用的任何版本出现问题,因此请务必小心,并可能考虑包含公差或将 foo 舍入到小数位数以消除微小的不确定性.
      • 但是您的评论更清楚。仅阅读答案,我无法理解您的观点。最好在你的答案中添加一个句子来解释它
      【解决方案4】:

      另一个:检查是否

      all(x == cummax(x))
      

      all(x == cummin(x))
      

      分别表示单调递增或递减。似乎cummaxdiff 快​​很多,而且使用的内存也更少:

      > 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
      

      我打赌为什么cummaxdiff 快是因为它只需要比较数字,这比计算差异要快。

      编辑:应您(阿里)的要求,包括您的答案在内的其他测试(请注意,我现在正在另一台机器上运行,因此不应将以下结果与上述结果进行比较)

      > 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 
      

      【讨论】:

        【解决方案5】:

        对于增加版本,您可以使用is.unsorted()

        x <- seq_len(1e7)
        !is.unsorted(x)
        
        > !is.unsorted(x)
        [1] TRUE
        

        这也很快:

        > 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
        

        不幸的是,is.unsorted() 明确用于递增顺序。我们在将其转换为下降情况时受到了一些打击,但它仍然与我系统上的其他选项相比具有竞争力:

        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
        

        还有一个更大的问题……

        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
        

        如果您想强制执行严格递增的序列,请参阅?is.unsorted 中的strictly

        【讨论】:

        • 这特别好,因为(它可能)一旦找到未排序的值就返回,而不是计算整个向量。
        • 它也会检测因子水平或其内容的无序,但当无序水平与无序值相同时则不会。
        • @AndriSignorell, !is.unsorted(rev(x)),虽然速度不是很好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-17
        • 2019-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-25
        相关资源
        最近更新 更多