algorithm - Convert Branch and Bound loop to use Java Stream API -
i have simple branch , bound algorithm works on variant of traveling salesman problem , thought fun try , convert use java 8 stream api. i'm having difficult time figuring out how without relying on side effects, however.
initial code
int bound = integer.max_value; list<location> bestpath = null; while(!queue.isempty()) { node curr = queue.poll(); //bound exceeds best, bail if (curr.getbound() >= bound) { return bestpath; } //have complete path, save if(curr.getpath().size() == locations.size()) { bestpath = curr.getpath(); bound = curr.getbound(); continue; } //incomplete path - add possible next steps set<location> unvisited = new hashset<>(locations); unvisited.removeall(curr.getpath()); (location l : unvisited) { list<location> newpath = new arraylist<>(curr.getpath()); newpath.add(l); node newnode = new node(newpath, getboundforpath(newpath)); if (newnode.getbound() <= bound){ queue.add(newnode); } } }
i took first shot @ converting stream api , came following:
java 8 version
consumer<node> nodeconsumer = node -> { if(node.getpath().size() == locations.size() ) { bestpath = node.getpath(); bound = node.getbound(); } else { locations.stream() .filter(l -> !node.getpath().contains(l)) .map(l -> { list<location> newpath = new arraylist<>(node.getpath()); newpath.add(s); return new node(newpath, getboundforpath(newpath)); }) .filter(newnode -> newnode.getbound() <= bound) .foreach(queue::add); } }; stream.generate(() -> queue.poll()) .peek(nodeconsumer) .filter(s -> s.getbound() > bound) .findfirst(); return bestpath;
the main problem nodeconsumer has reference bestpath , bound, not final variables. make them final atomicreference variables work around this, feel sort of violates spirit of stream api. can me distill initial algorithm more idiomatic implementation?
i wonder if using reduce
way go this, allows track values without need external variables.
something following (i've had guess @ few of details of above code, i'm on right track).
final bifunction<entry<integer, list<location>>, node, entry<integer, list<location>>> accumulator = (identity, node) -> { if (node.getpath().size() == locations.size() ) { return new simpleentry<>(node.getbound(), node.getpath()); } else { locations.stream() .filter(l -> !node.getpath().contains(l)) .map(l -> { list<location> newpath = new arraylist<>(node.getpath()); newpath.add(l); return new node(newpath, getboundforpath(newpath)); }) .filter(newnode -> newnode.getbound() <= identity.getkey()) .foreach(queue::add); return identity; } }; final binaryoperator<entry<integer, list<location>>> combiner = (left, right) -> left.getkey() < right.getkey() ? left : right; final entry<integer, list<location>> identity = new simpleentry<>(integer.max_value, null); final list<location> bestvalue = stream.generate(queue::poll) .reduce(identity, accumulator, combiner) .getvalue();
alternatively, @ using seq
in jooλ (a sequential extension streams), , use foldleft
instead.
Comments
Post a Comment