我遇到以下问题,并且不知道如何在R中编码:
包含两列的数据框df
:第一列是有序数字集df$ind
,第二列是高度随机数字df$ret
。
我想找到df$ret
中不重叠的两个范围,并优化第一范围中数字之和必须为正且第二范围数之和为负的条件。之后,我希望得到相应范围的2x2 df$ind
- 值。
我想到了两个可能性(我不知道如何在R中编写其中任何一个):
你能给我一些提示如何在R中实现这一点,或者是否有一个用于这些优化的包(似乎有一个包用于R中的所有内容; - )
更新
您将拥有4个值:i
,k
,m
,n
:df$ret[i:k]
和df$ret[m:n]
i
< k
< m
< n
。
优化是(伪代码):
最大:ABS(总和(范围(I:K)))+ ABS(总和(范围(M:N)))
在条件下:
总和(范围(i:k))> 0和sum(范围(m:n))< 0
答案 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$ind
与1: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秒内解决了一百万个元素。