如何优化可在图形中找到所有最大匹配的算法?

时间:2019-06-30 14:26:27

标签: swift algorithm optimization matching

在我的应用程序中,人们互相评分,满分为10分。每天,算法都会为尽可能多的人计算一个匹配(不可能为每个人都计算一个匹配)。它创建了一个图形,其中顶点是用户,边是坡度

我简化了这个问题,说两个人互相给一个分数,那么他们之间就会以各自平均水平的权重获得优势。但是,如果A给B评分,但B却没有,那么它们之间就没有优势,而且它们再也无法匹配:这样,图形就不再定向了

我希望每个人平均都感到高兴,但与此同时,我希望尽可能少的人没有匹配项。

由于具有确定性,因此我制定了一种算法,可以在图中找到所有最大匹配项。我这样做是因为我认为我可以分析所有这些最大匹配并应用看起来像这样的值函数:

V(Matching) = exp(|M| / max(|M|)) * sum(weight of all Edge in M)

也就是说,如果匹配项的基数接近最大匹配项的基数,并且人与人之间的等级总和较高,则该匹配项是高价值的。我对| M | / max | M |的比率设置了指数函数因为我认为如果M低于0.8是一个大问题(因此,当| M | / max | M |达到0.8时,exp将被安排为高度降低V)

我会在V(M)最大的情况下进行匹配。虽然,最大的问题是我计算所有最大匹配的函数要花费很多时间。仅需15个顶点和20条边线,将花费近10分钟的时间... 这是算法(在Swift中):

import Foundation

struct Edge : CustomStringConvertible {
    var description: String {
        return "e(\(v1), \(v2))"
    }

    let v1:Int
    let v2:Int
    let w:Int?

    init(_ arrint:[Int])
    {
        v1 = arrint[0]
        v2 = arrint[1]
        w = nil
    }
    init(_ v1:Int, _ v2:Int)
    {
        self.v1 = v1
        self.v2 = v2
        w = nil
    }
    init(_ v1:Int, _ v2:Int, _ w:Int)
    {
        self.v1 = v1
        self.v2 = v2
        self.w  = w
    }
}


let mygraph:[Edge] =
[
    Edge([1, 2]),
    Edge([1, 5]),
    Edge([2, 5]),
    Edge([2, 3]),
    Edge([3, 4]),
    Edge([3, 6]),
    Edge([5, 6]),

    Edge([2,6]),
    Edge([4,1]),
    Edge([3,5]),
    Edge([4,2]),
    Edge([7,1]),
    Edge([7,2]),

    Edge([8,1]),
    Edge([9,8]),
    Edge([11,2]),
    Edge([11, 8]),
    Edge([12,13]),
    Edge([1,6]),
    Edge([4,7]),
    Edge([5,7]),
    Edge([3,5]),
    Edge([9,1]),
    Edge([10,11]),
    Edge([10,4]),
    Edge([10,2]),
    Edge([10,1]),
    Edge([10, 12]),


]

// remove all the edge and vertex "touching" the edges and vertex in "edgePath"
func reduce (graph:[Edge], edgePath:[Edge]) -> [Edge]
{

    var alreadyUsedV:[Int] = []

    for edge in edgePath
    {
        alreadyUsedV.append(edge.v1)
        alreadyUsedV.append(edge.v2)
    }


    return graph.filter({ edge in
        return alreadyUsedV.first(where:{ edge.v1 == $0 }) == nil && alreadyUsedV.first(where:{ edge.v2 == $0 }) == nil
    })

}


func findAllMaximalMatching(graph Gi:[Edge]) -> [[Edge]]
{

    var matchings:[[Edge]] = []

    var G = Gi // current graph (reduced at each depth)
    var M:[Edge] = [] // current matching being built
    var Cx:[Int] = [] // current path in the possibilities tree 
                      // eg : Cx[1] = 3 : for the depth 1, we are at the 3th edge 
    var d:Int = 0 // current depth

    var debug_it = 0


    while(true)
    {

        if(G.count == 0) // if there is no available edge in graph, it means we have a matching
        {
            if(M.count > 0) // security, if initial Graph is empty we cannot return an empty matching
            {
                matchings.append(M)
            }

            if(d == 0)
            {
                // depth = 0, we cannot decrement d, we have finished all the tree possibilities
                break
            }
            d = d - 1

            _ = M.popLast()
            G = reduce(graph: Gi, edgePath: M)

        }

        else
        {
            let indexForThisDepth = Cx.count > d ? Cx[d] + 1 : 0
            if(G.count < indexForThisDepth + 1)
            {
                // depth ended,

                _ = Cx.popLast()

                if( d == 0)
                {
                    break
                }

                d = d - 1
                _ = M.popLast()

                // reduce from initial graph to the decremented depth
                G = reduce(graph: Gi, edgePath: M)

            }
            else
            {
                // matching not finished to be built
                M.append( G[indexForThisDepth] )

                if(indexForThisDepth == 0)
                {
                    Cx.append(indexForThisDepth)
                }
                else
                {
                    Cx[d] = indexForThisDepth
                }

                d = d + 1
                G = reduce(graph: G, edgePath: M)
            }
        }

        debug_it += 1
    }


    print("matching counts : \(matchings.count)")
    print("iterations : \(debug_it)")

    return matchings

}


let m = findAllMaximalMatching(graph: mygraph)

// we have compute all the maximal matching, now we loop through all of them to find the one that has V(Mi) maximum
// ....

最后,我的问题是:如何优化此算法以找到所有最大匹配并在它们上计算我的值函数以在多项式时间内找到最适合我的应用程序的匹配?

1 个答案:

答案 0 :(得分:0)

由于问题很复杂,我可能会遗漏一些东西,但是为什么不简单地使用maximum flow problem呢,每个顶点出现两次,并且边缘权重是存在的平均等级?如果配置正确,它将返回最大流量并运行多项式时间。