組み合わせを列挙する
SRMとかやってると組み合わせをみる必要があったりして、そのたびに同じようなコードを書いていたので、なんとか出来ないかと思って書いてみた。
import java.util.*; public class Combinations<T> implements Iterator{ private List<List<T>> combinations; private List<T> list; private int[] index; private boolean[] visited; private int r; private Iterator<List<T>> iterator; public Combinations(T[] array, int r){ this.list = Arrays.asList(array); this.index = new int[r]; this.visited = new boolean[array.length]; this.combinations = new ArrayList<List<T>>(); this.r = r; this.compute(0); this.iterator = this.combinations.iterator(); } private void compute(int n){ if(n == r){ List<T> combination = new ArrayList<T>(); for(int i = 0;i < this.index.length;i++){ combination.add(list.get(index[i])); } combinations.add(combination); } else{ for(int i = 0;i < this.list.size();i++){ if(n == 0 || !this.visited[i] && index[n - 1] < i){ this.visited[i] = true; this.index[n] = i; this.compute(n + 1); this.visited[i] = false; } } } } public List<List<T>> getCombinations(){ return this.combinations; } public int count(){ return this.combinations.size(); } public List<T> next(){ return this.iterator.next(); } public boolean hasNext(){ return this.iterator.hasNext(); } public Iterator<List<T>> iterator(){ return this.iterator; } public void remove(){ } //test public static void main(String[] argv){ String[] test = new String[]{ "a", "b", "c", "d", "e", "f", "g" }; Combinations<String> c = new Combinations<String>(test, 2); Iterator<List<String>> i = c.iterator(); while(i.hasNext()){ List<String> t = i.next(); System.out.println(t.get(0) + " " + t.get(1)); } System.out.println(c.count() + " petterns."); } }
結果:
$ java Combinations a b a c a d a e a f a g b c b d b e b f b g c d c e c f c g d e d f d g e f e g f g 21 petterns.
多分合ってる。
ちょっと大きい数字でやるとすぐ計算時間が膨大になる気がする。
追記:
100C100とかやってもすぐ帰ってきてほしいので、改良した。
import java.util.*; public class Combinations<T> implements Iterator{ private List<List<T>> combinations; private List<T> list; private int[] index; private boolean[] visited; private int r; private boolean overHalf; private Iterator<List<T>> iterator; public Combinations(T[] array, int r) throws IllegalArgumentException { if(array.length < 1 || r < 1 || array.length < r){ throw new IllegalArgumentException(); } this.combinations = new ArrayList<List<T>>(); this.list = Arrays.asList(array); this.r = r; if(this.r == array.length){ this.combinations.add(list); } else{ if(this.r > list.size() / 2){ this.r = list.size() - this.r; this.overHalf = true; } this.index = new int[this.r]; this.visited = new boolean[list.size()]; this.compute(0); } this.iterator = this.combinations.iterator(); } private void compute(int n){ if(n == this.r){ List<T> combination = new ArrayList<T>(); if(overHalf){ for(int i = 0;i < this.list.size();i++){ boolean skip = false; for(int j = 0;j < this.index.length;j++){ if(i == this.index[j]){ skip = true; } } if(skip){ continue; } combination.add(list.get(i)); } } else{ for(int i = 0;i < this.index.length;i++){ combination.add(list.get(index[i])); } } this.combinations.add(combination); } else{ for(int i = 0;i < this.list.size();i++){ if(n == 0 || !this.visited[i] && index[n - 1] < i){ this.visited[i] = true; this.index[n] = i; this.compute(n + 1); this.visited[i] = false; } } } } public List<List<T>> getCombinations(){ return this.combinations; } public static int count(int n, int r) throws IllegalArgumentException{ if(n < 1 || r < 1 || n < r){ throw new IllegalArgumentException(); } int res = 1; for(int i = n;i > r;i--){ res *= i; } for(int i = n - r;i > 1;i--){ res /= i; } return res; } public List<T> next(){ return this.iterator.next(); } public boolean hasNext(){ return this.iterator.hasNext(); } public Iterator<List<T>> iterator(){ return this.iterator; } public void remove(){ } //test public static void main(String[] argv){ String[] test = new String[]{ "a", "b", "c", "d", "e", "f", "g" }; int r = 5; Combinations<String> c = new Combinations<String>(test, r); Iterator<List<String>> it = c.iterator(); while(it.hasNext()){ List<String> comb = it.next(); for(int i = 0;i < comb.size();i++){ System.out.print(comb.get(i) + " "); } System.out.println(""); } System.out.println(Combinations.count(test.length, r) + " petterns."); } }
100C99っていうのは100C1と計算結果的には同じなので、rが半分より多い場合は「選ばないものを選ぶ」という感じになっている。100C99とかやっても割と早く帰ってくると思う。