一些集合算法
转自http://bbs.csdn.net/topics/370006637
SetAlgorithm.cs
1 using System; 2 namespace Rabbit.Tools 3 { 4 public static class SetAlgorithms 5 { 6 /// <summary> 7 /// 集合算法的回调 8 /// </summary> 9 /// <param name="result">运算结果</param> 10 /// <param name="length">运算结果有效长度</param> 11 /// <returns>控制算法是否继续,如果要结束算法,返回false</returns> 12 /// <remarks>回调中不要修改result中的值,否则可能引起不可预知的后果</remarks> 13 public delegate bool SetAlgorithmCallback (int[] result,int length); 14 15 //argument check for arrangement and combination 16 static bool CheckNM(int n, int m) 17 { 18 if (m > n || m < 0 || n < 0) 19 throw new ArgumentException(); 20 21 if (m == 0 || n == 0) 22 return false; 23 return true; 24 } 25 26 static bool Arrangement(int n, int rlen, int[] result, SetAlgorithmCallback callback) 27 { 28 if (rlen == result.Length) 29 return callback(result, rlen); 30 31 for (var i = 0; i < n; ++i) 32 { 33 //skip used element 34 bool skip = false; 35 36 for (var j = 0; j < rlen; ++j) 37 { 38 if (result[j] == i) 39 { 40 skip = true; 41 break; 42 } 43 } 44 45 if (skip) 46 continue; 47 //set element index 48 result[rlen] = i; 49 //recurrent next 50 if (!Arrangement(n, rlen + 1, result, callback)) 51 return false; 52 } 53 return true; 54 } 55 /// <summary> 56 /// 求排列A(n,m) 57 /// </summary> 58 /// <param name="n">集合元素个数</param> 59 /// <param name="m">取出元素个数</param> 60 /// <param name="callback">回调</param> 61 public static void Arrangement(int n, int m, SetAlgorithmCallback callback) 62 { 63 64 if (!CheckNM(n, m)) 65 return; 66 67 var result = new int[m]; 68 for (var i = 0; i < n; ++i) 69 { 70 result[0] = i; 71 if (!Arrangement(n, 1, result, callback)) 72 return; 73 } 74 } 75 76 static bool Combination(int n,int m, int i, int rlen, int[] result, SetAlgorithmCallback callback) 77 { 78 if (rlen == m) 79 return callback(result, rlen); 80 81 for (var j = ++i; j < n; ++j) 82 { 83 result[rlen] = j; 84 if (!Combination(n,m, j, rlen + 1, result, callback)) 85 return false; 86 } 87 return true; 88 } 89 /// <summary> 90 /// 求组合C(n,m) 91 /// </summary> 92 /// <param name="n">集合元素个数</param> 93 /// <param name="m">取出元素个数</param> 94 /// <param name="callback">回调</param> 95 public static void Combination(int n, int m, SetAlgorithmCallback callback) 96 { 97 if (!CheckNM(n, m)) 98 return; 99 100 int[] result; 101 102 result = new int[n]; 103 for (var i = 0; i < n; ++i) 104 { 105 result[0] = i; 106 107 if (!Combination(n,m, i, 1, result,callback)) 108 return; 109 } 110 } 111 112 static bool SubSet(int n, int i, int rlen, int[] result, SetAlgorithmCallback callback) 113 { 114 if (!callback(result, rlen)) 115 return false; 116 117 if (rlen == n - 1) 118 return true; 119 120 for (var j = ++i; j < n; ++j) 121 { 122 result[rlen] = j; 123 if (!SubSet(n, j, rlen + 1, result, callback)) 124 return false; 125 } 126 return true; 127 } 128 /// <summary> 129 /// 求除空集外包含n个元素的集合的真子集 130 /// </summary> 131 /// <param name="n">集合元素个数</param> 132 public static void SubSet(int n, SetAlgorithmCallback callback) 133 { 134 if (n < 0) 135 throw new ArgumentException(); 136 if (n == 0) 137 return; 138 139 var result = new int[n - 1]; 140 for (var i = 0; i < n; ++i) 141 { 142 result[0] = i; 143 if (!SubSet(n, i, 1, result, callback)) 144 return; 145 } 146 } 147 148 static bool CartesianProduct(int[] sets, int i, int[] result, SetAlgorithmCallback callback) 149 { 150 for (var j = 0; j < sets[i]; ++j) 151 { 152 result[i] = j; 153 if (i == sets.Length - 1) 154 { 155 if (!callback(result, result.Length)) 156 return false; 157 } 158 else 159 { 160 if (!CartesianProduct(sets, i + 1, result, callback)) 161 return false; 162 } 163 } 164 return true; 165 } 166 /// <summary> 167 /// 求集合笛卡尔积 168 /// </summary> 169 /// <param name="sets">包含集合元素个数的数组</param> 170 /// <param name="callback">回调函数</param> 171 public static void CartesianProduct(int[] sets, SetAlgorithmCallback callback) 172 { 173 int[] result = new int[sets.Length]; 174 CartesianProduct(sets, 0, result, callback); 175 } 176 } 177 }
提供了以下几个通用的算法:
求排列 求组合 求集合真子集 求集合笛卡尔积
没啥技术性,主要是泛用性.
例如: 已知3个集合 {"帽子1","帽子2","帽子3"} {"上衣1","上衣2","上衣3"} {"裤子a","裤子b"}
求所有的着装组合.可以使用笛卡尔积算法:
1 var c1= new string[]{"帽子1","帽子2","帽子3"}; 2 var c2 = new string[] { "上衣1", "上衣2", "上衣3" }; 3 var c3 = new string[] { "裤子a", "裤子b" }; 4 SetAlgorithms.CartesianProduct(new int[] { c1.Length, c2.Length, c3.Length }, (result, len) => 5 { 6 Console.WriteLine("{0},{1},{2}", c1[result[0]], c2[result[1]], c3[result[2]]); 7 return true; 8 });
其余的调用方法也类似. 举个更实际的例子,例如这帖的问题: http://topic.csdn.net/u/20110527/15/3f9ef827-988c-454c-8bf4-c44f48ec1fa2.html
例如,商品属性:
品牌: 海尔 LG 三星 索尼 夏普 TCL 创维 长虹 飞利浦 康佳
尺寸: 60寸以上 55寸 52寸 5 0寸 47寸 46寸 42寸
液晶面板 : IPS硬屏面板 VA面板 ASV面板 黑水晶面板 X-GEN超晶面板
每个属性都是一个集合,列举出所有筛选可能,包括只选一个条件,现在是3个属性,如果属性个数不确定怎么实现
对于这个问题.除去有未选的情况,我可以将结果集看做几个集合的笛卡尔集. 那对于未选状态怎么处理呢?我可以人为的为它的头部加上一个"未选"元素.例如品牌就变成了:
未选 海尔 LG 三星 索尼 夏普 TCL 创维 长虹 飞利浦 康佳
1 var bands = new string[] { "海尔", "LG", "三星", "索尼", "夏普", "TCL", "创维", "长虹", "飞利浦", "康佳" }; 2 3 var size = new string[] { "60寸以上", "55寸", "52寸", "50寸", "47寸", "46寸", "42寸" }; 4 5 var types = new string[] {"IPS硬屏面板", "VA面板", "ASV面板", "黑水晶面板", "X-GEN超晶面板" }; 6 7 var array = new string[][] { bands, size, types }; 8 SetAlgorithms.CartesianProduct(new int[] { bands.Length + 1, size.Length + 1, types.Length + 1 }, (result, len) => 9 { 10 for (var i = 0; i < array.Length; ++i) 11 { 12 if (result[i] == 0)//跳过"未选" 13 continue; 14 Console.Write("{0} ", array[i][result[i]-1]); 15 } 16 Console.WriteLine(); 17 return true; 18 });