递归穷举
概念
穷举是从指定的一组数据m中,选取出n个,然后对选取出的数据做加工计算(如求和、求可能结果等),根据选取数据的规则可以分为三种情况:
- 可重复的选取,有m^n种可能性,即第1位可以从m个数据中选一个,第2位依然可以从m个数据中选一个,…,第n位依然可以从m个数据中选一个,总的可能性为:m*m*…*m 。(n个m相乘)
- 排列选取,有m!/(m-n)!种可能性,即第1位可以从m个数据中选一个,第2位可以从m-1个数据中选一个,…,第n位可以从m-n+1个数据中选一个,总的可能性为:m*(m-1)*…*(m-n+1)。(是从m个数据中,选n,考虑顺序的情况下的可能性)
- 组合,m中选出n,不考虑顺序,可能的选法。有m!/[(m-n)!*n!]种可能性,即第1位可以从m-n+1个数据中选一个,第2位依然可以从第一个选择的数据后的位置选择一个数据(因为第1个选择的数据位置前的数据,已经被之前的穷举覆盖到),…,第n位可以从n-1个数据中选一个,此为算法考虑的思路。考虑总的可能性时,需要转换思路:排序是考虑顺序的一种数据选取方式,通过排序得到的可能性,存在有同一组数据的不同排列方式,若有n个数据,重复的方式为n!,因此,组合的可能性为排序可能性除以重复的排列方式,即总的可能性为:m!/[(m-n)!*n!]。(是从m个数据中,选n,不考虑顺序的情况下的可能性,在考虑顺序的情况下,把重复的数据去掉存在的可能性)
算法
如果仅需要知道存在的可能性个数,通过上面的计算公式,即可计算;下面是假设要找到所有的可能性数据列表,给出的算法。
上述的可重复穷举、排列、组合。均是从m个元素中选取n个,区别在于每次选取的数据的范围不同,可重复选取每次都能从m个中选,排列只能从未选的数据中选择,组合则是从当前选择数据的后一个开始选择。可以通过递归的方式实现,循环体为从未选列表中选择一个数据,添加到已选列表,递归的终止条件未已经选择出n个元素(递归的深度递减为0),递归方法的入参有三个:
- 未选的数据列表:List noSelect
- 已选择的数据列表:List select
- 当前剩余的递归次数: int deep;(深度将为0或者当前处理的深度为1时,则为最后一次递归)
java代码实现
/**
* 测试主方法
*/
public static void main(String[] args) {
List<String> noSelect = Arrays.asList("1", "2", "3");
List<String> select = new ArrayList<>();
// chongFu_QIONGJU(noSelect, select, 2);
// paiLie_QIONGJU(noSelect, select, 2);
zuHe_QIONGJU(noSelect, select, 2);
}
/**
* 允许重复的可能性
* @param noSelect 未选中的列表
* @param select 选中的列表
* @param deep 递归深度
*/
private static void chongFu_QIONGJU(List<String> noSelect,List<String> select,int deep) {
for (String one: noSelect) {
List<String> newSelect = new ArrayList<>();
copyList(select, newSelect, 0, -1);
newSelect.add(one);
int temp = deep -1;
if (0 == temp) {
System.out.println(newSelect);
} else {
chongFu_QIONGJU(noSelect, newSelect, temp);
}
}
}
/**
* 排列穷举算法
*/
private static void paiLie_QIONGJU(List<String> noSelect,List<String> select,int deep) {
for (int i = 0; i < noSelect.size(); i++) {
String one = noSelect.get(i);
List<String> newSelect = new ArrayList<>();
copyList(select, newSelect, 0, -1);
newSelect.add(one);
int temp = deep -1;
if (0 == temp) {
System.out.println(newSelect);
} else {
List<String> newNoSelect = new ArrayList<>();
copyList(noSelect, newNoSelect, 0, i);
paiLie_QIONGJU(newNoSelect, newSelect, temp);
}
}
}
/**
* 组合穷举算法
*/
private static void zuHe_QIONGJU(List<String> noSelect,List<String> select,int deep) {
for (int i = 0; i < noSelect.size()-deep+1; i++) {
String one = noSelect.get(i);
List<String> newSelect = new ArrayList<>();
copyList(select, newSelect, 0, -1);
newSelect.add(one);
int temp = deep -1;
if (0 == temp) {
System.out.println(newSelect);
} else {
List<String> newNoSelect = new ArrayList<>();
copyList(noSelect, newNoSelect, i, i);
zuHe_QIONGJU(newNoSelect, newSelect, temp);
}
}
}
/**
* 拷贝满足条件的集合
* @param source
* @param target
* @param pos
* @param noCopyPos
*/
public static void copyList(List<String> source, List<String> target, int pos , int noCopyPos) {
for (int i= pos ; i< source.size(); i++) {
if(i != noCopyPos) {
target.add(source.get(i));
}
}
}
进制转换
十进制与二进制、八进制、十六进制的转换
算法实现
-
其他进制转化为十进制(按权位展开)
如二进制数,其权重从右至左一次为1,2,4,…
转换为十进制方法为:右边第一位*1+右边第二位*2+右边第三位*4+…
"10101"转为方式为:11+02+14+08+1*16=21 -
十进制转换为其他进制(对进制数不断取余,余数为地位直至余数为零)
如十进制21,转换为8进制,21%8得5,八进制最低为5
21/8取整得2,2%8得2,则次低位为2,
2/8取整得0,终止,得到八进制数025
java代码实现
/**
* 二进制转十进制
*/
private static void test2To10() {
String num = "10101";
String temp = num; //处理中的数字字符串
int tempPos = num.length(); // 处理位数
final int WEIGHT = 2;
int posWeight = 1;
int result = 0; //转换后的结果
while (tempPos-- > 0) {
result += Integer.parseInt(temp.substring(tempPos)) * posWeight;
posWeight *= WEIGHT;
temp = temp.substring(0, tempPos);
}
System.out.println(result);
}
/**
* 十进制转二进制
*/
private static void test10To2() {
int num = 21;
final int WEIGHT = 2;
StringBuffer resultBuffer = new StringBuffer("");
while(0 != num) {
int i = num % WEIGHT;
resultBuffer.insert(0,i);
num = num / WEIGHT;
}
System.out.println(resultBuffer);
}
java现有的实现
Interger对象提供有进制转换的方法:
- 十进制转换为其他进制: 16 toH 8 toO 2 toB
- 其他进制转换为十进制: parseInt(字符串, 进制); valueOf(字符串,进制); 定义其他进制整数:16 0x 8 0 20b,直接输出则为十进制
/**
* 进制转换算法:
* toH toO toB
* parseInt
* 0x 0 0b
*/
private static void jinZhiZhuanHuan() {
System.out.println(Integer.toHexString(31));
System.out.println(Integer.toOctalString(16));
System.out.println(Integer.toBinaryString(16));
System.out.println(Integer.parseInt("1f",16));
System.out.println(Integer.parseInt("20",8));
System.out.println(Integer.parseInt("10101",2));
System.out.println(Integer.valueOf("10101",2));
int i = 0b10101; // 0x 0 0b
System.out.println(i);
}