子集算法的一个简单实现

看了《子集算法的完整数学推导过程》这篇文章后,感觉里面的数学公式很多,较难转换成计算机程序。

于是自己想了一个思路:把关系表达式看做是区间,比如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 }

 

比较复杂的是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(-5false), Right = new Endpoint(-2true) });
 7             a.Add(new Segment { Left = new Endpoint(1false), Right = new Endpoint(6true) });
 8 
 9             Set b = new Set();
10             b.Add(new Segment { Left = new Endpoint(-3false), Right = new Endpoint(0true) });
11             b.Add(new Segment { Left = new Endpoint(4false), Right = new Endpoint(6true) });
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

 

以上代码只是对一维的区间的实现,也就是关系表达式里只有一个变量的情况。如果有多个维度,在现在的基础上做一些扩展也能实现。

 

posted @ 2010-05-21 18:52  刘俊峰  阅读(1363)  评论(0编辑  收藏  举报