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