Java实现使用位图生成真值组合
摘要: 使用位图生成真值组合。
难度: 初级。
/** * 问题描述: 给定 n 个布尔变量,打印所有真值组合。 * 例如, n = 2 时 , 所有真值组合为 (true, false),(true, true),(false, true),(false, false). * * 算法的基本思路: * 使用一个长度为 n 的数组存储着 n 个布尔变量;位 1 表示 true , 位 0 表示 false, * 生成每一个真值元组,实际上是生成一个由 0 和 1 表示的 数组。 * * 生成每一个真值元组的方法:从零开始,逐次加一。 * 比如 000 -> 001 -> 010 -> 011 -> 100 -> 101 -> 110 -> 111 * * 具体算法: * 每次都从最低位开始,将最低位作【置 一】处理 : * ① 如果最低位是 0 , 则置 1 即可【不进位】; * ② 如果最低位为 1 , 则置 0 ; 由于有进位,进一步将次低位作【置一】处理。 * 直至某一位由 0 置 1 为止 【不进位】。 * * 例如: 011 : * ① 最低位为1, 置 0 , 并进位; * ② 次低位为1, 置 0 , 并进位; * ③ 次次低位为 0 , 置 1。 结果为 100 * * * 技巧: * ① 由于这里只涉及置 1 或 置 0 , 实际上就是置 true 或 置 false , * 因此, 可以直接在数组里存储布尔值,并不必要在 1,0 和 true, false 之间转换。 * * ② 设置一个结束标识变量 endflag ,当 1..1 -> 0..0 时 设置为 true * */ package algorithm; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Combination { private boolean[] combination ; private long count; private boolean endflag; public Combination(int n) { if (n <= 0) throw new IllegalArgumentException("参数必须为正整数"); if (combination == null) { combination = new boolean[n]; count = 0; endflag = false; } } /** * 求解问题,打印所有的真值组合结果。 * */ public void solution() { System.out.println("n = " + combination.length + " ***** 所有真值组合: "); do { System.out.println(getOneTuple()); count++; increOne(); } while(!terminate()); System.out.println("真值组合数: " + count); } /** * 逐次加一,生成每一个真值元组 * */ private void increOne() { int i; for (i=0; i < combination.length; i++) { // 若为 0 ,则置 1 , 结束。 if (combination[i] == false) { combination[i] = true; break; } else { // 若为 1, 则置 0, 并通过 i++ 转至次低位进行相同处理 combination[i] = false; } } // 由 1..1 -> 0..0 时, 设置 endflag = true; if (i == combination.length) { endflag = true; } } /** * 根据整数数组表示生成的真值元组,转化为布尔数组表示生成的真值元组。 * */ private String getOneTuple() { StringBuilder tuple = new StringBuilder("("); for (int i=0; i < combination.length; i++) { tuple.append(combination[i]); tuple.append(","); } // 删除 多余的 逗号 tuple.deleteCharAt(tuple.length()-1); tuple.append(")"); return tuple.toString(); } /** * 终止条件: 结束标识符 endflag = true; * */ private boolean terminate() { return endflag == true; } public static void main(String[] args) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); try { String s = null; while ((s = stdin.readLine()).matches("[1-9][0-9]*")) { int n = Integer.parseInt(s); System.out.println("n = " + n); Combination c = new Combination(n); c.solution(); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } // Combination c = new Combination(3); // c.solution(); } }
算法分析:
总的运行时间由两部分组成: 置一处理时间 和 判断结束时间。
T(n) = setBit(n) + judgeEnd(n)
其中: judgeEnd(n) = 2^n ,因为从 0..0 -> 1..1 -> 0..0 每次变换做一个简单的比较操作,endflag == true ,总共花时 2^n
下面计算 setBit(n) :
n = 1 时 0 -> 1 setBit(1) = 1;
n = 2 时 00-> 01 -> 10 -> 11 setBit(2) = 121 【置位次数:1+2+1】
n = 3 时 000 –> 001 -> 010 -> 011 -> 100 -> 101 -> 110 -> 111 setBit(3) = 1213121
n = 4 时 0000 -> 0001 -> 0010 -> 0011 -> 0100 -> 0101 -> 0110 -> 0111 -> 1000 -> 1001 -> .. -> 1111 setBit(4) = 121312141213121
归纳可得:
setBit(n) = n + 2setBit(n-1) setBit(1) = 1 ; 解得: setBit(n) = O(n^2)
故 T(n) = 2^n