开发者

Optimization problem - finding a maximum

开发者 https://www.devze.com 2023-01-08 20:05 出处:网络
I have a problem at hand which can be reduced to something like this : Assume a bunch of random points in a two-dimension plane X-Y where for each Y, there could be multiple points on X and for each

I have a problem at hand which can be reduced to something like this :

Assume a bunch of random points in a two-dimension plane X-Y where for each Y, there could be multiple points on X and for each X, there could be multiple points on Y.

Whenever a point (Xi,Yi) is chosen, no other point with X = Xi OR Y = Yi can be ch开发者_如何学运维osen. We have to choose the maximum number of points.


This can be reduced to simple maximum flow problem. If you have a point (xi, yi), in graph it should be represented with path from source S to point xi, from xi to yi and from yi to the last node (sink) T.

Note, if we have points (2, 2) and (2, 5), there's still only one path from S to x2. All paths (edges) have capacity 1.

The flow in this network is the answer.

about general problem
http://en.wikipedia.org/wiki/Max_flow

update
I don't have graphic editor right now to visualise problem, but you can easily draw example by hand. Let's say, points are (3, 3) (3, 5) (2, 5)

Then edges (paths) would be
S -> x2, S -> x3
y3 -> T, y5 -> T
x3 -> y3, x3 -> y5, x2 -> y5

Flow: S -> x2 -> y5 -> T and S -> x3 -> y3 -> T
The amount of 'water' going from source to sink is 2 and so is the answer.

Also there's a tutorial about max flow algorithms
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=maxFlow


Isn't this just the Hungarian algorithm?

Create an n×n matrix, with 0 at marked vertices, and 1 at unmarked vertices. The algorithm will choose n vertices, one for each row and column, which minimizes their sum. Simply count all the chosen vertices which equal 0, and you have your answer.

from munkres import Munkres

matrix = [[0, 0, 1],
          [0, 1, 1],
          [1, 0, 0]]

m = Munkres()
total = 0
for row, column in m.compute(matrix):
    if matrix[row][column] == 0:
        print '(%i, %i)' % (row, column)
        total += 1

print 'Total: %i' % total

This runs in O(n3) time, where n is the number of rows in the matrix. The maximum flow solution runs in O(V3), where V is the number of vertices. As long as there are more than n chosen intersections, this runs faster; in fact, it runs orders of magnitude faster, as the number of chosen vertices goes up.


Different solution. It turns out that there's a lot of symmetry, and the answer is a lot simpler than I originally thought. The maximum number of things you can ever do is the minimum of the unique X's and unique Y's, which is O(NlogN) if you just want the result.

Every other shape is equivalent to a rectangle that contains the points, because it doesn't matter how many points you pull from the center of a rectangle, the order will never matter (if handled as below). Any shape that you pluck a point from now has one less unique X and one less unique Y, just like a rectangle.

So the optimal solution has nothing to do with connectedness. Pick any point that is on the edge of the smallest dimension (i.e. if len(unique-Xs)>len(unique-Ys), pick anything that has either maximum or minimum X). It doesn't matter how many connections it has, just which dimension is biggest, which can easily be done while looking at the sorted-unique lists created above. If you keep a unique-x and unique-y counter and decrement them when you delete all the unique nodes in that element of the list, then each deletion is O(1) and recalculating the lengths is O(1). So repeating this N times is at worst O(N), and the final complexity is O(NlogN) (due solely to the sorting).

You can pick any point along the shortest edge because:

  • if there's only one on that edge, you better pick it now or something else will eliminate it
  • if there's more than one on that edge, who cares, you will eliminate all of them with your pick anyways

Basically, you're maximizing "max(uniqX,uniqY)" at each point.

Update: IVlad caught an edge case:

If the dimensions are equal, take the edge with the least points. Even if they aren't equal, take the top or bottom of the unique-stack you're eliminating from that has the least points.

Case in point:

Turn 1:

  • Points: (1, 2); (3, 5); (10, 5); (10, 2); (10, 3)
  • There are 3 unique X's: 1, 3, 10
  • There are 3 unique Y's: 2, 3, 5
  • The "bounding box" is (1,5),(10,5),(10,2),(1,2)

Reaction 1:

  • The "outer edge" (outermost uniqueX or uniqueY lists of points) that has the least points is the left. Basically, look at the sets of points in x=1,x=10 and y=2,y=5. The set for x=1 is the smallest: one point. Pick the only point for x=1 -> (1,2).
  • That also eliminates (10,2).

Turn 2:

  • Points: (3, 5); (10, 5); (10, 3)
  • There are 2 unique X's: 3, 10
  • There are 2 unique Y's: 3, 5
  • The "bounding box" is (3,5),(10,5),(10,3),(3,3)

Reaction 2:

  • The "edge" of the bounding box that has the least is either the bottom or the left. We reached the trivial case - 4 points means all edges are the outer edges. Eliminate one. Say (10,3).
  • That also eliminates (10,5).

Turn 3:

  • Points: (3, 5)

Reaction 3:

  • Remove (3,5).


For each point, identify the number of other points (N) that would be disqualified by the selection of that point (i.e. the ones with the same X or Y values). Then, iterate over the non-disqualified points in order of increasing number of N disqualified points. When you are finished, you will have removed the maximum number of points.


The XY plane is a red herring. Phrase it as a set of elements, each of which has a set of mutually exclusive elements.

The algorithm then becomes a depth-first search. At each level, for each candidate node, calculate the set of excluded elements, the union of currently excluded elements with the elements excluded by the candidate node. Try candidate nodes in order of fewest excluded elements to most. Keep track of the best solution so far (the fewest excluded nodes). Prune any subtrees that are worse than the current best.

As a slight improvement at the cost of possible missed solutions, you can use Bloom filters for keeping track of the excluded sets.


This looks like a problem that can be solved with dynamic programming. Look into the algorithms for longest common substring, or the knapsack problem.


Based on a recommendation from IVlad, I looked into the Hopcroft–Karp algorithm. It's generally better than both the maximum flow algorithm and the Hungarian algorithm for this problem, often significantly. Some comparisons:

In general:

  • Max Flow: O(V3) where V is the number of vertices.
  • Hungarian: O(n3) where n is the number of rows in the matrix
  • Hopcroft-Karp: O(V √2V) where V is the number of vertices.

For a 50×50 matrix, with 50% chosen vertices:

  • Max Flow: 1,2503 = 1,953,125,000
  • Hungarian: 503 = 125,000
  • Hopcroft-Karp: 1,250 √2,500 = 62,500

For a 1000×1000 matrix, with 10 chosen vertices:

  • Max Flow: 103 = 1,000
  • Hungarian: 10003 = 1,000,000,000
  • Hopcroft-Karp: 10 √20 ≅ 44.7

The only time the Hungarian algorithm is better is when there is a significantly high proportion of points selected.

For a 100×100 matrix, with 90% chosen vertices:

  • Max Flow: 9,0003 = 729,000,000,000
  • Hungarian: 1003 = 1,000,000
  • Hopcroft-Karp: 9,000 √18,000 ≅ 1,207,476.7

The Max Flow algorithm is never better.

It's also quite simple, in practice. This code uses an implementation by David Eppstein:

points = {
    0 : [0, 1],
    1 : [0],
    2 : [1, 2],
}

selected = bipartiteMatch(points)[0]

for x, y in selected.iteritems():
    print '(%i, %i)' % (x, y)

print 'Total: %i' % len(selected)
0

精彩评论

暂无评论...
验证码 换一张
取 消