具有非重复元素的最小ID

时间:2014-03-07 11:55:44

标签: r

我陷入了R中的一个难题,我无法解决它。问题是这样的。

x和y是两个向量,如下所示:

x<- c(1,2,3,4,5)
y<- c(12,4,2,5,7,18,9,10)

我想以下列方式创建一个新的向量p,其中length(p)= length(x):

  • 对于x中的每个id,在y中找到具有最小绝对距离的id。例如,对于x中的id = 1,value_x(id = 1)= 1,min_value_y = 2,并且id_y(value == 2)= 3.因此,x中id 1的答案是3.因此,我们创建一个新的向量p,它将具有以下值:p =(3,3,3,2,4);

现在我们必须以下列方式更新p:

  • 由于3是对应于id_x = 1的id,因此它不能是id_x = 2的id。因此,我们必须丢弃值为2的id_y = 3,以计算id_x = 2的下一个最小距离。 id_x = 2的下一个最佳最小距离是id_y = 2,值为4.因此,更新的p是(3,2,3,2,4)。
  • 由于3是对应于id_x = 1的id,因此它不能是id_x = 3的id。因此,我们必须丢弃值为2的id_y = 3,以计算id_x = 3的下一个最小距离。 id_x = 3的下一个最佳最小距离是2.因此,更新的p是(3,2,4,2,4)。

由于p中的下一个值是2和4,我们必须重复我们在最后两个步骤中所做的操作。总之,在计算x和y之间的最小距离时,对于x的每个id,我们必须得到之前未出现的y的id。因此p的所有元素必须是唯一的。

任何答案都将不胜感激。

我试过这样的事情,虽然不是一个完整的解决方案:

minID <- function(x,y) {return(which(abs(x-y)==min(abs(x-y))))};
p1 <- sapply(x,minID,y=y);
#Calculates the list of all minimum elements -no where close to actual solution :(

我的x和y超过100万,因此循环会非常慢。我正在寻找更快的解决方案。

3 个答案:

答案 0 :(得分:9)

这可以通过y元素上的二元搜索树有效实现,删除元素,因为它们匹配并添加到p。我使用C ++中的stl使用set实现了这一点,使用Rcpp将代码放入R:

library(Rcpp)
getVals = cppFunction(
'NumericVector getVals(NumericVector x, NumericVector y) {
    NumericVector p(x.size());
    std::vector<std::pair<double, int> > init;
    for (int j=0; j < y.size(); ++j) {
        init.push_back(std::pair<double, int>(y[j], j));
    }
    std::set<std::pair<double, int> > s(init.begin(), init.end());
    for (int i=0; i < x.size(); ++i) {
        std::set<std::pair<double, int> >::iterator p1, p2, selected;
        p1 = s.lower_bound(std::pair<double, int>(x[i], 0));
        p2 = p1;
        --p2;
        if (p1 == s.end()) {
            selected = p2;
        } else if (p2 == s.begin()) {
            selected = p1;
        } else if (fabs(x[i] - p1->first) < fabs(x[i] - p2->first)) {
            selected = p1;
        } else {
            selected = p2;
        }
        p[i] = selected->second+1;  // 1-indexed
        s.erase(selected);
    }
    return p;
}')

这是与已发布的纯R解决方案的运行时比较 - 二进制搜索树解决方案更快,并且只需几秒钟即可启用长度为100万的向量的解决方案:

# Pure-R posted solution
getVals2 = function(x, y) {
  n <- length(x)
  p <- rep(NA, n)
  for(i in 1:n) {
    id <- which.min(abs(y - x[i]))
    y[id] <- Inf
    p[i] <- id
  }
  return(p)
}

# Test with medium-sized vectors
set.seed(144)
x = rnorm(10000)
y = rnorm(20000)
system.time(res1 <- getVals(x, y))
#    user  system elapsed 
#   0.008   0.000   0.008 
system.time(res2 <- getVals2(x, y))
#    user  system elapsed 
#   1.284   2.919   4.211 
all.equal(res1, res2)
# [1] TRUE

# Test with large vectors
set.seed(144)
x = rnorm(1000000)
y = rnorm(2000000)
system.time(res3 <- getVals(x, y))
#    user  system elapsed 
#   4.402   0.097   4.467 

加速的原因是因为这种方法渐近更快 - 如果x的大小为ny大小为m,那么二进制搜索树方法在O((n+m)log(m))时间 - O(m log(m))运行以构建BST,O(n log(m))计算p - 而which.min方法在O(nm)运行时间。

答案 1 :(得分:3)

n <- length(x)
p <- rep(NA, n)
for(i in 1:n) {
  id <- which.min(abs(y - x[i]))
  y[id] <- Inf
  p[i] <- id
}

答案 2 :(得分:0)

我尝试在R中开发代码,并且在for循环中获得了大约20倍的改进。这段代码如下:

Generalized.getMinId <- function(a,b)
{
     sapply(a, FUN = function(x) which.min(abs(x-b)))
}

Generalized.getAbsDiff <- function(a,b)
{
     lapply(a, FUN = function(x) abs(x-b))
}

min_id = Generalized.getMinId(tlist,clist);
dup = which(duplicated(min_id));

while(length(dup) > 0)
{

absdiff = Generalized.getAbsDiff(tlist[dup],clist);
infind = lapply(dup, function(x,y)
                    {l <- head(y,x-1); l[l>0]}, y = min_id);

absdiff = Map(`[<-`, absdiff, infind, Inf);
dupind = sapply(absdiff, which.min);
min_id[dup] = dupind;

dup = which(duplicated(min_id));
}

如果有人可以对这段代码进行改进,那就太棒了。