Re: Re: Re: optimally picking one element from each list
- To: mathgroup at smc.vnet.net
- Subject: [mg48358] Re: [mg48324] Re: [mg48308] Re: [mg48297] optimally picking one element from each list
- From: Andrzej Kozlowski <akoz at mimuw.edu.pl>
- Date: Tue, 25 May 2004 07:17:36 -0400 (EDT)
- References: <200405220704.DAA08848@smc.vnet.net> <200405231015.GAA21155@smc.vnet.net> <200405240445.AAA08868@smc.vnet.net> <E2802AB0-AD75-11D8-9FDB-000A95B4967A@mimuw.edu.pl> <opr8itwntoiz9bcq@holycow.cox-internet.com>
- Sender: owner-wri-mathgroup at wolfram.com
It seems that I somehow sent a "pre-release" version of my code instead
fo the finished one. It should have been:
<< "DiscreteMath`Combinatorica`"
BacktrackPick[sp_] := Block[{n = Length[sp], c = Length[sp], w,
partialQ, solutionQ},
partialQ[l_List] := Which[Length[l] == n && (w = Length[Split[l]])
<= c, c = w; True,
Length[l] < n && Length[Split[l]] <= c, True, True, False];
solutionQ[l_List] := If[Length[l] == n && (w = Length[Split[l]]) <=
c, c = w; True, False];
Last[Backtrack[sp, partialQ, solutionQ, All]]]
Andrzej
On 25 May 2004, at 05:30, DrBob wrote:
> I got errors (see attached) before getting to any tests.
>
> Bobby
>
> On Mon, 24 May 2004 20:31:23 +0900, Andrzej Kozlowski
> <akoz at mimuw.edu.pl> wrote:
>
>> What errors do you get and on what examples? At least on the ones you
>> gave below my function works fine. I have not yet found one where it
>> fails.
>>
>> As I wrote in the original posting, I am sure it can be made faster if
>> someone wrote an optimised backtracking code rather than rely on the
>> Backtrack function form the Combinatorica package. But I do not wish
>> to
>> spend time on this.
>>
>> Using compiled optimizers ought to be faster, maybe much faster on the
>> whole, though perhaps not in all cases. Performance in this sort of
>> problem seems to depend very much on the starting configuration. For
>> example, if if the backtracking algorithm happens to find a "best"
>> solution early on it will finish much earlier than when it finds it
>> only near the end of the search.
>>
>>
>> Andrzej
>>
>> On 24 May 2004, at 13:45, DrBob wrote:
>>
>>> I get errors when I try Andrzej's function; maybe others had more
>>> luck?
>>>
>>> Here's a solution using Minimize. It may be wasteful in the setup,
>>> but
>>> solution will be very fast. I'm not certain a Sort isn't needed
>>> somewhere in the last statement, but this seems OK.
>>>
>>> An even faster method would solve a corresponding shortest path
>>> problem, if we have a solver available. I don't have to require or
>>> check for integer solutions below, because of that correspondence;
>>> basic optimal solutions will be integer automatically.
>>>
>>> Clear[choice, optimize]
>>> optimize[raw:{__List}] :=
>>> Module[{sets = Union /@ raw, choice, costBounds,
>>> choiceConstraints, pVar, costConstraints,
>>> objective, p, cVar, vars, bounds, solution},
>>> choice[set_, {index_}] :=
>>> Plus @@ (p[index, #1] & ) /@ set == 1;
>>> costBounds[{set1_, set2_}, {index_}] :=
>>> Module[{switches},
>>> switches = Plus @@ (p[index, #1] & ) /@
>>> Complement[set1, set2];
>>> {(cost[index] >= switches + (p[index, #1] -
>>> p[index + 1, #1]) & ) /@ Intersection[
>>> set1, set2], cost[index] >= switches}];
>>> choiceConstraints = Flatten[MapIndexed[choice,
>>> sets]]; pVar = Union[Cases[choiceConstraints,
>>> _p, Infinity]]; costConstraints =
>>> Flatten[MapIndexed[costBounds, Partition[sets, 2,
>>> 1]]]; cVar = Union[Cases[costConstraints,
>>> _cost, {2}]]; objective = Plus @@ cVar;
>>> vars = Flatten[{pVar, cVar}];
>>> bounds = Thread[0 <= vars <= 1];
>>> solution = Minimize[Flatten[{objective,
>>> choiceConstraints, costConstraints, bounds}],
>>> vars]; Cases[Last[solution] /. Rule -> rule,
>>> rule[p[_, a_], 1] :> a]]
>>>
>>> Timing@optimize@{{1,3},{2,3},{1,3},{1,3,4},{4,1}}
>>> Timing@optimize@{{6},{2},{2},{7,8},{3,4,1},{2,8,2},{5,2},{7},{8,5,8},
>>> {2
>>> ,1,
>>> 7},{5},{5}}
>>>
>>> {0.06299999999999999*Second, {3, 3, 3, 3, 1}}
>>> {0.015000000000000013*Second, {6, 2, 2, 7, 1, 2, 2, 7, 5, 1, 5, 5}}
>>>
>>> Bobby
>>>
>>> On Sun, 23 May 2004 06:15:35 -0400 (EDT), Andrzej Kozlowski
>>> <akoz at mimuw.edu.pl> wrote:
>>>
>>>>
>>>> On 22 May 2004, at 16:04, Daniel Reeves wrote:
>>>>
>>>>> Suppose you have a list of lists and you want to pick one element
>>>>> from
>>>>> each and put them in a new list so that the number of elements that
>>>>> are
>>>>> identical to their next neighbor is maximized.
>>>>> (in other words, for the resulting list l, minimize
>>>>> Length[Split[l]].)
>>>>> (in yet other words, we want the list with the fewest
>>>>> interruptions
>>>>> of
>>>>> identical contiguous elements.)
>>>>>
>>>>> EG, pick[{ {1,2,3}, {2,3}, {1}, {1,3,4}, {4,1} }]
>>>>> --> { 2, 2, 1, 1, 1 }
>>>>>
>>>>> Here's a preposterously brute force solution:
>>>>>
>>>>> pick[x_] := argMax[-Length[Split[#]]&, Distribute[x, List]]
>>>>>
>>>>> where argMax can be defined like so:
>>>>>
>>>>> (* argMax[f,domain] returns the element of domain for which f of
>>>>> that element is maximal -- breaks ties in favor of first
>>>>> occurrence.
>>>>> *)
>>>>> SetAttributes[argMax, HoldFirst];
>>>>> argMax[f_, dom_List] := Fold[If[f[#1] >= f[#2], #1, #2] &,
>>>>> First[dom], Rest[dom]]
>>>>>
>>>>> Below is an attempt at a search-based approach, which is also way
>>>>> too
>>>>> slow. So the gauntlet has been thrown down. Anyone want to give
>>>>> it
>>>>> a
>>>>> shot?
>>>>>
>>>>>
>>>>> (* Used by bestFirstSearch. *)
>>>>> treeSearch[states_List, goal_, successors_, combiner_] :=
>>>>> Which[states=={}, $Failed,
>>>>> goal[First[states]], First[states],
>>>>> True, treeSearch[
>>>>> combiner[successors[First[states]],
>>>>> Rest[states]],
>>>>> goal, successors, combiner]]
>>>>>
>>>>> (* Takes a start state, a function that tests whether a state is a
>>>>> goal
>>>>> state, a function that generates a list of successors for a
>>>>> state,
>>>>> and
>>>>> a function that gives the cost of a state. Finds a goal state
>>>>> that
>>>>> minimizes cost.
>>>>> *)
>>>>> bestFirstSearch[start_, goal_, successors_, costFn_] :=
>>>>> treeSearch[{start}, goal, successors,
>>>>> Sort[Join[#1,#2], costFn[#1] < costFn[#2] &]&]
>>>>>
>>>>> (* A goal state is one for which we've picked one element of every
>>>>> list
>>>>> in l.
>>>>> *)
>>>>> goal[l_][state_] := Length[state]==Length[l]
>>>>>
>>>>> (* If in state s we've picked one element from each list in l up to
>>>>> list
>>>>> i, then the successors are all the possible ways to extend s to
>>>>> pick
>>>>> elements thru list i+1.
>>>>> *)
>>>>> successors[l_][state_] := Append[state,#]& /@ l[[Length[state]+1]]
>>>>>
>>>>> (* Cost function g: higher cost for more neighbors different
>>>>> (Length[Split[state]]) and then breaks ties in favor of longer
>>>>> states to keep from unnecessarily expanding the search tree.
>>>>> *)
>>>>> g[l_][state_] :=
>>>>> Length[Split[state]]*(Length[l]+1)+Length[l]-Length[state]
>>>>>
>>>>> (* Pick one element from each of the lists in l so as to minimize
>>>>> the
>>>>> cardinality of Split, ie, maximize the number of elements that
>>>>> are
>>>>> the same as their neighbor.
>>>>> *)
>>>>> pick[l_] := bestFirstSearch[{}, goal[l], successors[l], g[l]]
>>>>>
>>>>>
>>>>> --
>>>>> http://ai.eecs.umich.edu/people/dreeves - - google://"Daniel
>>>>> Reeves"
>>>>>
>>>>> If you must choose between two evils,
>>>>> pick the one you've never tried before.
>>>>>
>>>>>
>>>>>
>>>>
>>>> Here is an approach which, while not really fast, at least is much
>>>> faster than yours. It will correctly minimize Length[Split[l]], but
>>>> will not necessarily give the same answer as your approach because I
>>>> have not bothered to make sure that ties are settled in the way you
>>>> indicated. It should not be hard to modify it to do so but I did not
>>>> really wish to spend time on this matter.
>>>>
>>>> The idea is to use the Backtrack function from
>>>> DiscreteMath`Combinatorica`.
>>>>
>>>> Here is the program:
>>>>
>>>> <<DiscreteMath`Combinatorica`
>>>>
>>>> BacktrackPick[l_]:= Block[{n = Length[sp], c = Length[sp], w},
>>>> partialQ[l_List] := Which[Length[l] == n && (w = Length[Split[
>>>> l]]) <= c, c = w; True, Length[l] < n &&
>>>> Length[Split[l]] <= c, True, True, False];
>>>> solutionQ[l_List] := If[Length[l] == n && (w =
>>>> Length[Split[l]])
>>>> <=
>>>> c,
>>>> c = w; True, False]; Last[Backtrack[sp, partialQ,
>>>> solutionQ,
>>>> All]]]
>>>>
>>>>
>>>> To see how this compares with yours let's create a random test case:
>>>>
>>>>
>>>>
>>>> sp=Table[Table[Random[Integer,{1,9}],{Random[Integer,{1,3}]}],{12}]
>>>>
>>>>
>>>> {{6},{2},{2},{7,8},{3,4,1},{2,8,2},{5,2},{7},{8,5,8},{2,1,7},{5},{5}
>>>> }
>>>>
>>>> Let's try your pick function:
>>>>
>>>>
>>>> Length[Split[sol1=pick[sp]]]//Timing
>>>>
>>>>
>>>> {15.33 Second,9}
>>>>
>>>> Now BacktrackPick:
>>>>
>>>>
>>>> Length[Split[sol2=BacktrackPick[sp]]]//Timing
>>>>
>>>>
>>>> {0.34 Second,9}
>>>>
>>>> Quite a difference. This time the answers happened to be the same:
>>>>
>>>>
>>>> sol1
>>>>
>>>> {6,2,2,8,1,2,2,7,8,7,5,5}
>>>>
>>>> sol2
>>>>
>>>> {6,2,2,8,1,2,2,7,8,7,5,5}
>>>>
>>>> But this will not be the case in general.
>>>>
>>>> I am sure that one can write a customised backtracking solution that
>>>> will be much faster than mine. However, writing customised (that is
>>>> without using Backtrack) backtracking programs is a bit of an art
>>>> that
>>>> requires patience and care and I have a short supply of both of
>>>> these.
>>>> So I am cc-ing this message to a well known member of this list who
>>>> is
>>>> a real master in this sort of thing ;-) so he might be interested in
>>>> taking up the challenge.
>>>>
>>>> Andrzej Kozlowski
>>>>
>>>>
>>>
>>>
>>>
>>> --
>>> Using M2, Opera's revolutionary e-mail client:
>>> http://www.opera.com/m2/
>>>
>>>
>>
>>
>
>
>
> --
> Using M2, Opera's revolutionary e-mail client:
> http://www.opera.com/m2/<Andrzej.nb>
- References:
- optimally picking one element from each list
- From: Daniel Reeves <dreeves@umich.edu>
- Re: optimally picking one element from each list
- From: Andrzej Kozlowski <akoz@mimuw.edu.pl>
- Re: Re: optimally picking one element from each list
- From: DrBob <drbob@bigfoot.com>
- optimally picking one element from each list