Javaでプリキュア冪集合(DX3)
お題:
べき集合ということは、各要素について選ぶか/選ばないかの組み合わせを全通り数え上げたものってことで、つまり二分木をトラバースする問題だな。
「『ある金額になるコインの組み合わせ』 に挑戦 - コードの恵み」を真似してTree iteratorで実装してみる。
べき集合のイテレータを戻すクラス
Guava r0.8を使用しています。
import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterators; public final class PowerSetUtils { // 状態を表すオブジェクト public static class Node<T> { // 選択された要素の一覧 public final Set<T> selected; // 未選択の要素の候補の一覧 public final Set<T> rest; public Node(Set<T> selected, Set<T> rest) { this.selected = selected; this.rest = rest; } } // 途中の状態も含めて全てのノードを戻すイテレータ public static class PowerSetIterator<T> implements Iterator<Node<T>> { // 状態を保持するスタック private final LinkedList<Node<T>> stack = new LinkedList<Node<T>>(); public PowerSetIterator(Collection<T> values) { // 初期状態は、全て未選択 stack.push(new Node<T>(Collections.<T>emptySet(), new LinkedHashSet<T>(values))); } @Override public boolean hasNext() { return !stack.isEmpty(); } @Override public Node<T> next() { Node<T> node = stack.pop(); if (!node.rest.isEmpty()) { // 未選択の候補を一つ取り出す T item = node.rest.iterator().next(); // 選択済み要素に候補を追加したもの Set<T> selected = new LinkedHashSet<T>(node.selected); selected.add(item); // 未選択のリストから候補一つを取り除いた残り Set<T> rest = new LinkedHashSet<T>(node.rest); rest.remove(item); // 状態を追加: 候補を選択しなかった場合 stack.push(new Node<T>(node.selected, rest)); // 状態を追加: 候補を選択した場合 stack.push(new Node<T>(selected, rest)); } return node; } @Override public void remove() { throw new UnsupportedOperationException(); } } // 状態のイテレータから、最終結果のイテレータを生成する。 public static <T> Iterator<Set<T>> powerSetIterator(Collection<T> values) { Iterator<Node<T>> iterator = new PowerSetIterator<T>(values); // 条件: 候補を全部使い切っている Predicate<Node<T>> predicate = new Predicate<Node<T>>() { @Override public boolean apply(Node<T> entry) { return entry.rest.isEmpty(); } }; // 結果の抽出: 選択された要素の一覧を取り出す Function<Node<T>, Set<T>> tran = new Function<Node<T>, Set<T>>() { @Override public Set<T> apply(Node<T> entry) { return entry.selected; } }; return Iterators.transform(Iterators.filter(iterator, predicate), tran); } }
実行
DX3で登場するプリキュア一覧
public enum Precure { キュアブラック, キュアホワイト, シャイニールミナス, キュアブルーム, キュアイーグレット, キュアドリーム, キュアルージュ, キュアレモネード, キュアミント, キュアアクア, ミルキィローズ, キュアピーチ, キュアベリー, キュアパイン, キュアパッション, キュアブロッサム, キュアマリン, キュアサンシャイン, キュアムーンライト, キュアメロディ, キュアリズム; }
各組み合わせを「、」区切りにして出力。StringUtilsはCommons Langのものデス。
public static void printPowerSetPrecure(PrintStream ps) throws IOException { Iterator<Set<Precure>> iterator = powerSetIterator(Arrays.asList(Precure.values())); while(iterator.hasNext()) { Set<Precure> precures = iterator.next(); ps.println(StringUtils.join(precures, "、")); } ps.flush(); } public static void main(String[] args) { try { printPowerSetPrecure(System.out); } catch (IOException e) { e.printStackTrace(); } }