优化R中的范围

时间:2011-12-13 11:13:49

标签: r optimization

我遇到以下问题,并且不知道如何在R中编码:

包含两列的数据框df:第一列是有序数字集df$ind,第二列是高度随机数字df$ret

我想找到df$ret中不重叠的两个范围,并优化第一范围中数字之和必须为正且第二范围数之和为负的条件。之后,我希望得到相应范围的2x2 df$ind - 值。

我想到了两个可能性(我不知道如何在R中编写其中任何一个):

  1. 蒙特卡洛,选择2x2位置,计算总和并与迄今为止最佳解决方案进行比较。
  2. 尝试所有可能的范围并采取最佳解决方案(关于值似乎在计算上可行的值的数量)。
  3. 你能给我一些提示如何在R中实现这一点,或者是否有一个用于这些优化的包(似乎有一个包用于R中的所有内容; - )

    更新
    您将拥有4个值:ikmndf$ret[i:k]df$ret[m:n] i< k< m< n

    优化是(伪代码):

      

    最大:ABS(总和(范围(I:K)))+ ABS(总和(范围(M:N)))


    在条件下:

      

    总和(范围(i:k))> 0和sum(范围(m:n))< 0

2 个答案:

答案 0 :(得分:4)

这是一种蛮力方法。对于小数据集,它应该工作正常;在我的系统上,我测试了大小100,它大约0.5秒。为了提高速度,在检查所有可能的最大/最小对之前,应检查重叠的最大最大值和最小值。

getbest <- function(x) {
  # get the sums of all possible ranges
  n <- length(x)
  m <- as.data.frame(t(combn(n, 2)))
  names(m) <- c("lo","hi")
  m$sum <- sapply(1:nrow(m), function(i) {
    sum(x[m$lo[i]:m$hi[i]])
  })
  # then get the ranges of positive and negative sums that don't overlap
  neg <- m[m$sum<0,]
  pos <- m[m$sum>0,]
  use <- expand.grid(neg=1:nrow(neg), pos=1:nrow(pos))
  use <- use[(neg$hi[use$neg] < pos$lo[use$pos]) | 
                  (neg$lo[use$neg] > pos$hi[use$pos]),]
  # finally get the absolute value for all ranges that don't overlap,
  # and choose the largest
  abs <- pos$sum[use$pos] - neg$sum[use$neg]
  use <- use[which.max(abs),]
  as.matrix(rbind(positive=pos[use$pos,], negative=neg[use$neg,]))
}

使用如下;它返回范围的实际索引,因此如果所需索引df$ind1:n不同,只需使用此输出即可获得所需的值。

x <- rnorm(100)
getbest(x)

答案 1 :(得分:2)

这个问题是Jon Bentleys庆祝“Programming Pearls”第7栏的主题。解决方案是运行时间为O(n)的算法,其中n是向量x的长度。

R实现可以在不到3秒的时间内解决数百万个元素的向量:

x <- rnorm(1e6)
system.time(m <- maxsub(x))

如果你想要负数,也可以调用maxsub(-x)。更改代码很容易,函数将返回索引。这两个范围不能重叠,但一个可以是另一个范围的一部分。

maxsub <- function(x) {
    if (!is.numeric(x))
        stop("Argument 'x' must be a numeric vector.")

    m1 <- m2 <- 0.0
    for (i in 1:length(x)) {
        m2 <- max(m2 + x[i], 0.0)
        m1 <- max(m1, m2)
    }
    return(m1)
}

虽然代码看起来很简单,但是直到有人提出这个解决方案需要相当长的时间,正如Bentley报道的那样。尚未找到针对二维(或更高维)情况的相应算法。

更新:这是一个也返回索引的版本。它看起来很复杂,但事实并非如此。它只是跟踪上述两个步骤中的每个指标。

maxsub <- function(x, inds = FALSE) {
    if (!is.numeric(x))
        stop("Argument 'x' must be a numeric vector.")
    n <- length(x)

    if (!inds) {
        m1 <- m2 <- 0.0
        for (i in 1:n) {
            m2 <- max(m2 + x[i], 0.0)
            m1 <- max(m1, m2)
        }
        return(m1)

    } else {
        m1 <- m2 <- 0
        p1 <- p2 <- 0
        q1 <- q2 <- 1

        for (i in 1:n) {
            if (m2 > -x[i]) {
                m2 <- m2 + x[i]
                q2 <- i
                if (m2 > m1) {
                    m1 <- m2
                    p1 <- q1; p2 <- q2
                }
            } else {
                m2 <- 0
                q1 <- q2 <- i+1
            }
        }

        return(list(sum = m1, inds = c(p1, p2)))
    }
}

与R包绑定的Fortran版本确实在0.015秒内解决了一百万个元素。