在列表中查找匹配对的算法

时间:2009-11-26 23:58:07

标签: algorithm optimization math combinatorics

我将在下面以我想要的精确形式说出问题:

鉴于:     相同长度N的两个浮点列表Dkk是2的倍数)。     众所周知,对于所有i=0,...,k-1j != i存在D[j]*D[i] == N[i]*N[j]。 (我使用的是从零开始的索引)

返回:     一对(k/2)对(i,j)列表,D[j]*D[i] == N[i]*N[j]。     返回的对可能不是唯一的(任何有效的对列表都可以)

该算法的应用是找到广义回文特征值问题的倒数对特征值。 平等条件等同于N[i]/D[i] == D[j]/N[j],但在分母为零时也是有效的(这是一种确定的可能性)。特征值问题中的简并导致这些对是非唯一的。

更一般地说,该算法相当于:

鉴于:     长度X的列表kk是2的倍数)。     众所周知,对于所有i=0,...,k-1,存在j != iIsMatch(X[i],X[j])返回true,其中IsMatch是布尔匹配函数,保证至少返回一个所有j != i都是i

返回:     一个(k/2}对(i,j)列表,列表中所有对的IsMatch(i,j) == true。     返回的对可能不是唯一的(任何有效的对列表都可以)

显然,我的第一个问题可以用IsMatch(u,v) := { (u - 1/v) == 0 }的第二个问题来表达。现在,由于浮点精度的限制,永远不会有完全相等,所以我想要最小化匹配误差的解决方案。换句话说,假设IsMatch(u,v)返回值u - 1/v,我希望算法返回一个列表,IsMatch返回最小的错误集。这是一个组合优化问题。我以为我可以先天真地计算所有可能的索引对ij之间的匹配错误,但后来我需要选择最小错误集,我不知道我是怎么做的会这样做。

澄清

IsMatch函数是自反的(IsMatch(a,b)暗示IsMatch(b,a)),但不是传递函数。但是,它是3-transitive:IsMatch(a,b) && IsMatch(b,c) && IsMatch(c,d)隐含IsMatch(a,d)

附录

这个问题显然与图论中最小权重完美匹配问题相同。但是,在我的情况下,我知道应该有一个“良好”的完美匹配,因此边权重的分布不是完全随机的。我觉得应该以某种方式使用这些信息。现在的问题是,对于最小权重完美匹配问题是否有良好的实现,该问题使用我的先前知识在搜索早期达到解决方案。我也对指向任何此类算法的简单实现持开放态度。

7 个答案:

答案 0 :(得分:1)

我希望我能解决你的问题。

好吧,如果是IsMatch(i, j) and IsMatch(j, l),那么IsMatch(i, l)。更一般地说,IsMatch关系是可传递的,可交换的和反身的,即。它是一种等价关系。该算法转换为列表中出现次数最多的元素(使用IsMatch而不是=)。

答案 1 :(得分:0)

(如果我理解这个问题......) 以下是匹配两个列表中每对产品的一种方法。

  1. 将每对N相乘并将其保存到产品结构中,以及构成产品的元素的下标。
  2. 将每对D相乘并将其保存到产品结构的第二个实例,以及构成产品的元素的下标。
  3. 对产品上的两个结构进行排序。
  4. 使合并类型传递两个已排序的结构数组。每次从一个阵列中找到与另一个阵列足够接近的产品时,您可以记录每个排序列表中的两个下标以进行匹配。
  5. 您还可以使用一个排序列表作为ismatch函数,对产品进行二进制搜索。

答案 2 :(得分:0)

嗯..将每对D相乘并将其保存到产品结构的第二个实例,以及构成产品的元素的下标。

答案 3 :(得分:0)

我刚问了我的CS朋友,他想出了下面的算法。他这里没有账户(显然不愿意创建账户),但我认为他的回答值得分享。

// We will find the best match in the minimax sense; we will minimize
// the maximum matching error among all pairs. Alpha maintains a
// lower bound on the maximum matching error. We will raise Alpha until
// we find a solution. We assume MatchError returns an L_1 error.

// This first part finds the set of all possible alphas (which are
// the pairwise errors between all elements larger than maxi-min
// error.
Alpha = 0
For all i:
    min = Infinity
    For all j > i:
        AlphaSet.Insert(MatchError(i,j))
        if MatchError(i,j) < min
            min = MatchError(i,j)
    If min > Alpha
        Alpha = min

Remove all elements of AlphaSet smaller than Alpha

// This next part increases Alpha until we find a solution
While !AlphaSet.Empty()
    Alpha = AlphaSet.RemoveSmallest()
    sol = GetBoundedErrorSolution(Alpha)
    If sol != nil
        Return sol

// This is the definition of the helper function. It returns
// a solution with maximum matching error <= Alpha or nil if
// no such solution exists.
GetBoundedErrorSolution(Alpha) :=
    MaxAssignments = 0
    For all i:
        ValidAssignments[i] = empty set;
        For all j > i:
            if MatchError <= Alpha
                ValidAssignments[i].Insert(j)
                ValidAssignments[j].Insert(i)

        // ValidAssignments[i].Size() > 0 due to our choice of Alpha
        // in the outer loop

        If ValidAssignments[i].Size() > MaxAssignments
            MaxAssignments = ValidAssignments[i].Size()
    If MaxAssignments = 1
        return ValidAssignments
    Else
        G = graph(ValidAssignments)
        // G is an undirected graph whose vertices are all values of i
        // and edges between vertices if they have match error less
        // than or equal to Alpha
        If G has a perfect matching
            // Note that this part is NP-complete.
            Return the matching
        Else
            Return nil

它依赖于能够计算完全匹配的图形,这是NP完全的,但至少它被简化为已知问题。预计解决方案是NP完全的,但这是可以的,因为在实践中给定列表的大小非常小。我会在几天内等待更好的答案,或者让某人扩展如何以合理的方式找到完美的匹配。

答案 4 :(得分:0)

你想找到j这样D(i)* D(j)= N(i)* N(j){我假设*是普通的实数乘法}

假设所有N(i)都非零,请

Z(i)= D(i)/ N(i)。

问题:找到j,使得Z(i)= 1 / Z(j)。

拆分设置为正数和负数并单独处理。

为了清晰起见,请记录日志。 z(i)= log Z(i)。

间接排序。然后在排序视图中,你应该有类似-5 -3 -1 +1 +3 +5的东西。读取+/-对,这应该给你原始索引。

我错过了什么,或问题很容易?

答案 5 :(得分:0)

好的,我最终使用this ported Fortran code,我只需使用以下命令指定密集的上三角距离矩阵:

complex_t num = N[i]*N[j] - D[i]*D[j];
complex_t den1 = N[j]*D[i];
complex_t den2 = N[i]*D[j];
if(std::abs(den1) < std::abs(den2)){
    costs[j*(j-1)/2+i] = std::abs(-num/den2);
}else if(std::abs(den1) == 0){
    costs[j*(j-1)/2+i] = std::sqrt(std::numeric_limits<double>::max());
}else{
    costs[j*(j-1)/2+i] = std::abs(num/den1);
}

这很有效,并且足够快,可以达到我的目的。

答案 6 :(得分:0)

您应该能够对(D [i],N [i])对进行排序。您不需要除以零 - 您可以将其乘出,如下所示:

bool order(i,j) {
  float ni= N[i]; float di= D[i];
  if(di<0) { di*=-1; ni*=-1; }

  float nj= N[j]; float dj= D[j];
  if(dj<0) { dj*=-1; nj*=-1; }

  return ni*dj < nj*di;
}

然后,扫描排序列表以找到两个分离点:(N == D)和(N == -D);你可以从那里开始匹配互惠对,使用:

abs(D[i]*D[j]-N[i]*N[j])<epsilon

作为有效性检查。保留(N == 0)和(D == 0)点为最后一个;无论你认为它们是消极还是积极都没关系,因为它们都会相互匹配。

编辑:或者,您可以单独处理(N == 0)和(D == 0)个案,将其从列表中删除。然后,您可以使用(N [i] / D [i])对其余索引进行排序。您仍然可能希望从1.0和-1.0开始,以确保您可以将接近零的情况与完全为零的情况匹配。