私はTraveling Salesmanの問題の変形で動作する単純なブランチアンドバウンドアルゴリズムを使用しています。これを試してみて、Java 8 Stream API 。私は、しかし、副作用に頼らずにそれを行う方法を考え出すのは難しい時があります。JavaストリームAPIを使用するためにブランチとバウンドループを変換する
初期コード
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 a complete path, save it
if(curr.getPath().size() == locations.size()) {
bestPath = curr.getPath();
bound = curr.getBound();
continue;
}
//incomplete path - add all possible next steps
Set<Location> unvisited = new HashSet<>(locations);
unvisited.removeAll(curr.getPath());
for (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);
}
}
}
私は、ストリームAPIに変換で最初のショットを取って、次のを思い付いた:
のJava 8バージョン
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;
主な問題は、nodeConsumerがbestPathとboundを参照しなければならないことです。最終的な変数ではありません。私は、これを回避するためにAtomicReference変数を最終的に設定することができましたが、この種のストリームAPIの精神に違反しているような気がします。初期のアルゴリズムをより慣用的な実装に汲み上げるのを誰かが助けてくれますか?
私はあなたがAPIを悪用することなくもっと良いものを得ることができるとは思わない。ストリームAPIはそのようなアルゴリズムには適していません。それにもかかわらず、問題は面白いです。 –
@TagirValeevお返事ありがとうございます。まだ私が利用できる新しいオプションに慣れていて、それが理想的ではないので、私が間違っているか難しいかを識別するのは難しいです。 –