看了《子集算法的完整数学推导过程》这篇文章后,感觉里面的数学公式很多,较难转换成计算机程序。
于是自己想了一个思路:把关系表达式看做是区间,比如3<x<=5是(3,5],逻辑与或运算就是区间的交集和并集运算。
然后写程序实现了区间的子集,交集和并集运算。区间(Segment)有两个端点(Endpoit),难点是多个区间的集会(Set)之间的运算。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace SubSetProblem
7 {
8 public struct Endpoint
9 {
10 public double Value;
11 public bool Inclusive;
12
13 public Endpoint(double value, bool inclusive)
14 {
15 Value = value;
16 Inclusive = inclusive;
17 }
18
19 public static bool operator <=(Endpoint a, Endpoint b)
20 {
21 return a.Value < b.Value ||
22 (a.Value == b.Value &&
23 !(a.Inclusive == true && b.Inclusive == false)
24 );
25 }
26
27 public static bool operator >=(Endpoint a, Endpoint b)
28 {
29 return a.Value > b.Value ||
30 (a.Value == b.Value &&
31 !(a.Inclusive == false && b.Inclusive == true)
32 );
33 }
34
35 public static bool operator <(Endpoint a, Endpoint b)
36 {
37 return !(a >= b);
38 }
39
40 public static bool operator >(Endpoint a, Endpoint b)
41 {
42 return !(a <= b);
43 }
44 }
45
46 public struct Segment
47 {
48 public Endpoint Left;
49 public Endpoint Right;
50
51 public static readonly Segment Empty = new Segment();
52
53 public override string ToString()
54 {
55 return String.Format(
56 "{0}{1}, {2}{3}", Left.Inclusive ? '[' : '(', Left.Value, Right.Value, Right.Inclusive ? ']' : ')');
57 }
58 }
59
60 public class Set : List<Segment>
61 {
62 public new void Add(Segment seg)
63 {
64 if (seg.Equals(Segment.Empty) == false)
65 {
66 base.Add(seg);
67 }
68 }
69
70 public override string ToString()
71 {
72 StringBuilder text = new StringBuilder();
73 text.Append('{');
74 this.ForEach(s => { text.Append(s.ToString()); text.Append(", "); });
75 if (this.Count > 0)
76 {
77 text.Length -= 2;
78 }
79 text.Append('}');
80 return text.ToString();
81 }
82 }
83
84 public class SetOperation
85 {
86 public static bool IsSubSet(Set a, Set b)
87 {
88 return a.Count(s => IsSubSet(s, b)) == a.Count;
89 }
90
91 public static bool IsSubSet(Segment seg, Set set)
92 {
93 return set.Count(s => IsSubSet(seg, s)) > 0;
94 }
95
96 public static bool IsSubSet(Segment a, Segment b)
97 {
98 return a.Left >= b.Left && a.Right <= b.Right;
99 }
100
101 public static Set Union(Set a, Set b)
102 {
103 Set union = b;
104 foreach (var segA in a)
105 {
106 union = Union(segA, union);
107 }
108 return union;
109 }
110
111 public static Set Intersect(Set a, Set b)
112 {
113 Set intersect = new Set();
114 foreach (var segA in a)
115 {
116 intersect = Union(intersect, Intersect(segA, b));
117 }
118 return intersect;
119 }
120
121 public static Set Union(Segment a, Set setB)
122 {
123 Set union = new Set();
124 foreach (var b in setB)
125 {
126 if (Intersect(a, b).Equals(Segment.Empty))
127 {
128 union.Add(b);
129 }
130 else
131 {
132 a = new Segment { Left = Min(a.Left, b.Left), Right = Max(a.Right, b.Right) };
133 }
134 }
135 union.Add(a);
136 return union;
137 }
138
139 public static Set Intersect(Segment segA, Set b)
140 {
141 Set intersect = new Set();
142 foreach (var segB in b)
143 {
144 intersect = Union(Intersect(segA, segB), intersect);
145 }
146 return intersect;
147 }
148
149 public static Segment Intersect(Segment a, Segment b)
150 {
151 if (a.Equals(Segment.Empty) || b.Equals(Segment.Empty))
152 {
153 return Segment.Empty;
154 }
155
156 var left = Max(a.Left, b.Left);
157 var right = Min(a.Right, b.Right);
158
159 if (left <= right)
160 {
161 return new Segment { Left = left, Right = right };
162 }
163 else
164 {
165 return Segment.Empty;
166 }
167 }
168
169 public static Set Union(Segment a, Segment b)
170 {
171 var left = Max(a.Left, b.Left);
172 var right = Min(a.Right, b.Right);
173
174 Set union = new Set();
175 if (left <= right)
176 {
177 union.Add(new Segment { Left = Min(a.Left, b.Left), Right = Max(a.Right, b.Right) });
178 }
179 else
180 {
181 union.Add(a);
182 union.Add(b);
183 }
184 return union;
185 }
186
187 public static Endpoint Min(Endpoint a, Endpoint b)
188 {
189 return a < b ? a : b;
190 }
191
192 public static Endpoint Max(Endpoint a, Endpoint b)
193 {
194 return a < b ? b : a;
195 }
196 }
197 }
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace SubSetProblem
7 {
8 public struct Endpoint
9 {
10 public double Value;
11 public bool Inclusive;
12
13 public Endpoint(double value, bool inclusive)
14 {
15 Value = value;
16 Inclusive = inclusive;
17 }
18
19 public static bool operator <=(Endpoint a, Endpoint b)
20 {
21 return a.Value < b.Value ||
22 (a.Value == b.Value &&
23 !(a.Inclusive == true && b.Inclusive == false)
24 );
25 }
26
27 public static bool operator >=(Endpoint a, Endpoint b)
28 {
29 return a.Value > b.Value ||
30 (a.Value == b.Value &&
31 !(a.Inclusive == false && b.Inclusive == true)
32 );
33 }
34
35 public static bool operator <(Endpoint a, Endpoint b)
36 {
37 return !(a >= b);
38 }
39
40 public static bool operator >(Endpoint a, Endpoint b)
41 {
42 return !(a <= b);
43 }
44 }
45
46 public struct Segment
47 {
48 public Endpoint Left;
49 public Endpoint Right;
50
51 public static readonly Segment Empty = new Segment();
52
53 public override string ToString()
54 {
55 return String.Format(
56 "{0}{1}, {2}{3}", Left.Inclusive ? '[' : '(', Left.Value, Right.Value, Right.Inclusive ? ']' : ')');
57 }
58 }
59
60 public class Set : List<Segment>
61 {
62 public new void Add(Segment seg)
63 {
64 if (seg.Equals(Segment.Empty) == false)
65 {
66 base.Add(seg);
67 }
68 }
69
70 public override string ToString()
71 {
72 StringBuilder text = new StringBuilder();
73 text.Append('{');
74 this.ForEach(s => { text.Append(s.ToString()); text.Append(", "); });
75 if (this.Count > 0)
76 {
77 text.Length -= 2;
78 }
79 text.Append('}');
80 return text.ToString();
81 }
82 }
83
84 public class SetOperation
85 {
86 public static bool IsSubSet(Set a, Set b)
87 {
88 return a.Count(s => IsSubSet(s, b)) == a.Count;
89 }
90
91 public static bool IsSubSet(Segment seg, Set set)
92 {
93 return set.Count(s => IsSubSet(seg, s)) > 0;
94 }
95
96 public static bool IsSubSet(Segment a, Segment b)
97 {
98 return a.Left >= b.Left && a.Right <= b.Right;
99 }
100
101 public static Set Union(Set a, Set b)
102 {
103 Set union = b;
104 foreach (var segA in a)
105 {
106 union = Union(segA, union);
107 }
108 return union;
109 }
110
111 public static Set Intersect(Set a, Set b)
112 {
113 Set intersect = new Set();
114 foreach (var segA in a)
115 {
116 intersect = Union(intersect, Intersect(segA, b));
117 }
118 return intersect;
119 }
120
121 public static Set Union(Segment a, Set setB)
122 {
123 Set union = new Set();
124 foreach (var b in setB)
125 {
126 if (Intersect(a, b).Equals(Segment.Empty))
127 {
128 union.Add(b);
129 }
130 else
131 {
132 a = new Segment { Left = Min(a.Left, b.Left), Right = Max(a.Right, b.Right) };
133 }
134 }
135 union.Add(a);
136 return union;
137 }
138
139 public static Set Intersect(Segment segA, Set b)
140 {
141 Set intersect = new Set();
142 foreach (var segB in b)
143 {
144 intersect = Union(Intersect(segA, segB), intersect);
145 }
146 return intersect;
147 }
148
149 public static Segment Intersect(Segment a, Segment b)
150 {
151 if (a.Equals(Segment.Empty) || b.Equals(Segment.Empty))
152 {
153 return Segment.Empty;
154 }
155
156 var left = Max(a.Left, b.Left);
157 var right = Min(a.Right, b.Right);
158
159 if (left <= right)
160 {
161 return new Segment { Left = left, Right = right };
162 }
163 else
164 {
165 return Segment.Empty;
166 }
167 }
168
169 public static Set Union(Segment a, Segment b)
170 {
171 var left = Max(a.Left, b.Left);
172 var right = Min(a.Right, b.Right);
173
174 Set union = new Set();
175 if (left <= right)
176 {
177 union.Add(new Segment { Left = Min(a.Left, b.Left), Right = Max(a.Right, b.Right) });
178 }
179 else
180 {
181 union.Add(a);
182 union.Add(b);
183 }
184 return union;
185 }
186
187 public static Endpoint Min(Endpoint a, Endpoint b)
188 {
189 return a < b ? a : b;
190 }
191
192 public static Endpoint Max(Endpoint a, Endpoint b)
193 {
194 return a < b ? b : a;
195 }
196 }
197 }
比较复杂的是Set Union(Set a, Set b)和Set Intersect(Set a, Set b)这两个方法,请大家验证有无错误。
接下来做了一个简单的测试:
1 public class Test
2 {
3 public static string Test1()
4 {
5 Set a = new Set();
6 a.Add(new Segment { Left = new Endpoint(-5, false), Right = new Endpoint(-2, true) });
7 a.Add(new Segment { Left = new Endpoint(1, false), Right = new Endpoint(6, true) });
8
9 Set b = new Set();
10 b.Add(new Segment { Left = new Endpoint(-3, false), Right = new Endpoint(0, true) });
11 b.Add(new Segment { Left = new Endpoint(4, false), Right = new Endpoint(6, true) });
12
13 Set u = SetOperation.Union(a, b);
14 Set i = SetOperation.Intersect(a, b);
15
16 return u + " " + i + " " + SetOperation.IsSubSet(i, u);
17 }
18 }
2 {
3 public static string Test1()
4 {
5 Set a = new Set();
6 a.Add(new Segment { Left = new Endpoint(-5, false), Right = new Endpoint(-2, true) });
7 a.Add(new Segment { Left = new Endpoint(1, false), Right = new Endpoint(6, true) });
8
9 Set b = new Set();
10 b.Add(new Segment { Left = new Endpoint(-3, false), Right = new Endpoint(0, true) });
11 b.Add(new Segment { Left = new Endpoint(4, false), Right = new Endpoint(6, true) });
12
13 Set u = SetOperation.Union(a, b);
14 Set i = SetOperation.Intersect(a, b);
15
16 return u + " " + i + " " + SetOperation.IsSubSet(i, u);
17 }
18 }
返回结果是:{(-5, 0], (1, 6]} {(4, 6], (-3, -2]} True
以上代码只是对一维的区间的实现,也就是关系表达式里只有一个变量的情况。如果有多个维度,在现在的基础上做一些扩展也能实现。