我正在寻找一种检测冗余规则的算法。
规则具有固定数量的输入参数,每个参数都有一个不同的域。
考虑三个规则参数颜色,材质和尺寸:
每个规则可以匹配参数的多个值或匹配任何值。选择与所有参数值匹配的第一个规则。没有否定规则,但域是固定的,因此可以通过添加所有其他域来实现否定。
+--------------------------------------------------++-----------------
| Rule Parameters || Rule Action
+----------------+------------------+--------------++-----------------
| Color | Material | Size || ==> Price
+----------------+------------------+--------------++-----------------
Rule 1 | Red | Wood, Glass | Large || $100
Rule 2 | Red | Aluminium | Large || $200
Rule 3 | Blue or Green | Wood | Medium || $300
Rule 4 | Red | ** Any ** | Large || $400 <== Redundant
Rule 5 | ** Any ** | ** Any ** | ** Any ** || $500
由于规则1和规则2的组合,规则4是多余的。冗余规则是从未匹配的规则,因为之前定义的规则(组合)那个规则。冗余检查中不评估规则操作。
如何有效地实施 (在Java中)?它应该支持1000条规则,每条参数有10个参数,每个参数100个从数据库中读取规则,参数和参数值(即,它们不能被硬编码)。
效率的问题在于 100 ^ 10种组合的输入参数(每个参数都有100个值的域)。规则编辑器gui中需要该算法,以支持创建规则的用户。它应该在几秒钟内找到所有冗余规则。
我已经创建了一个存储库来测试提议的解决方案:https://github.com/qlp/redundant-rules 目前只有一个BDD实现,它失败了这个大小的问题。也许我的BDD实现可以改进?
答案 0 :(得分:1)
编辑由于评论完全重写。 (请注意,这实际上可能类似于Khaled A Khunaifer所写的内容,但它已同时创建)
一种可能的方法是找到规则的“逻辑”描述。规则有一个非常规则的形式,即始终是分离的结合。规则可以写成
(Red) AND (Wood OR Glass) AND (Large)
(Red) AND (Aluminium) AND (Large)
...
现在,可以应用复杂的优化和最小化(如Quine McCluskey或任何其他形式的minimization)。但是,我在这里概述了一个非常简单的实现。当规则仅在其中一个分离中有所不同时,可以“合并”规则。例如,上面的规则可以合并到
(Red) AND (Wood OR Glass OR Aluminium) AND (Large)
现在,如果像
这样的规则(Red) AND (Wood OR Glass OR Aluminium) AND (Medium)
遇到了,它可以与前一个合并到
(Red) AND (Wood OR Glass OR Aluminium) AND (Large OR Medium)
如果我们找到了像
这样的规则(Red) AND (Glass OR Aluminium) AND (Medium)
它可以被标记为“冗余”,因为它是前一个隐含的。
再次强调这一点:这个实现相当“hacky”并且远非“优雅”,并且肯定有许多可能的改进。但也许它表明总体思路至少是可行的。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class RulesChecker
{
public static void main(String[] args)
{
List<Rule> rules = new ArrayList<Rule>();
List<Set<String>> domains = new ArrayList<Set<String>>();
domains.add(setOf("Red", "Green", "Blue"));
domains.add(setOf("Wood", "Glass", "Aluminium"));
domains.add(setOf("Small", "Medium", "Large"));
// Rule 1 | Red | Wood, Glass | Large || $100
// Rule 2 | Red | Aluminium | Large || $200
// Rule 3 | Blue or Green | Wood | Medium || $300
// Rule 4 | Red | ** Any ** | Large || $400 <== Redundant
// Rule 5 | ** Any ** | ** Any ** | ** Any ** || $500
rules.add(new Rule("$100", disOf("Red") , disOf("Wood", "Glass"), disOf("Large")));
rules.add(new Rule("$200", disOf("Red") , disOf("Aluminium") , disOf("Large")));
rules.add(new Rule("$300", disOf("Blue", "Green"), disOf("Wood") , disOf("Medium")));
rules.add(new Rule("$310", disOf("Blue") , disOf("Wood") , disOf("Medium")));
rules.add(new Rule("$350", disOf("Green") , disOf("Aluminium") , disOf("Medium")));
rules.add(new Rule("$400", disOf("Red") , disOf(domains.get(1)) , disOf("Large")));
rules.add(new Rule("$500", disOf(domains.get(0)) , disOf(domains.get(1)) , disOf(domains.get(2))));
System.out.println("Rules: ");
for (Rule rule : rules)
{
System.out.println(rule);
}
List<Rule> mergedRules = new ArrayList<Rule>();
mergedRules.add(rules.get(0));
for (int i=1; i<rules.size(); i++)
{
add(mergedRules, rules.get(i));
}
}
private static void add(List<Rule> mergedRules, Rule newRule)
{
for (int i=0; i<mergedRules.size(); i++)
{
Rule oldRule = mergedRules.get(i);
if (implies(oldRule, newRule))
{
System.out.println("Redundant "+newRule);
System.out.println(" due to "+oldRule);
return;
}
int mergeIndex = mergeIndex(oldRule, newRule);
if (mergeIndex != -1)
{
Rule mergedRule = merge(oldRule, newRule, mergeIndex);
mergedRules.set(i, mergedRule);
System.out.println("Merging "+oldRule);
System.out.println(" and "+newRule);
System.out.println(" gives "+mergedRule);
return;
}
}
mergedRules.add(newRule);
}
private static boolean implies(Rule oldRule, Rule newRule)
{
Conjunction c0 = oldRule.conjunction;
Conjunction c1 = newRule.conjunction;
List<Expression> es0 = new ArrayList<Expression>(c0.terms);
List<Expression> es1 = new ArrayList<Expression>(c1.terms);
for (int i=0; i<es0.size(); i++)
{
Disjunction d0 = (Disjunction) es0.get(i);
Disjunction d1 = (Disjunction) es1.get(i);
if (!d0.terms.containsAll(d1.terms))
{
return false;
}
}
return true;
}
private static Disjunction disOf(String ... ss)
{
return disOf(Arrays.asList(ss));
}
private static Disjunction disOf(Collection<String> ss)
{
List<Variable> list = new ArrayList<Variable>();
for (String s : ss)
{
list.add(new Variable(s));
}
return new Disjunction(list);
}
private static int mergeIndex(Rule r0, Rule r1)
{
Conjunction c0 = r0.conjunction;
Conjunction c1 = r1.conjunction;
List<Expression> es0 = new ArrayList<Expression>(c0.terms);
List<Expression> es1 = new ArrayList<Expression>(c1.terms);
int different = 0;
int mergeIndex = -1;
for (int i=0; i<es0.size(); i++)
{
Expression e0 = es0.get(i);
Expression e1 = es1.get(i);
if (!e0.equals(e1))
{
mergeIndex = i;
different++;
if (different > 1)
{
return -1;
}
}
}
return mergeIndex;
}
private static Rule merge(Rule r0, Rule r1, int index)
{
Conjunction c0 = r0.conjunction;
Conjunction c1 = r1.conjunction;
List<Expression> es0 = new ArrayList<Expression>(c0.terms);
List<Expression> es1 = new ArrayList<Expression>(c1.terms);
Set<Disjunction> rc = new LinkedHashSet<Disjunction>();
for (int i=0; i<es0.size(); i++)
{
Disjunction d0 = (Disjunction) es0.get(i);
Disjunction d1 = (Disjunction) es1.get(i);
if (i == index)
{
Set<Expression> merged = new LinkedHashSet<Expression>();
merged.addAll(d0.terms);
merged.addAll(d1.terms);
Disjunction d = new Disjunction(merged);
rc.add(d);
}
else
{
rc.add(d0);
}
}
return new Rule("TRUE", new Conjunction(rc));
}
private static Set<String> setOf(String ... s)
{
return new LinkedHashSet<String>(Arrays.asList(s));
}
static class Expression
{
}
static class Variable extends Expression
{
final String name;
Variable(String name)
{
this.name = name;
}
@Override
public String toString()
{
return name;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Variable other = (Variable) obj;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}
static class Disjunction extends Expression
{
private final Set<Expression> terms;
Disjunction(Collection<? extends Expression> expressions)
{
this.terms = new LinkedHashSet<Expression>(expressions);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("(");
int n = 0;
for (Expression e : terms)
{
sb.append(e);
n++;
if (n < terms.size())
{
sb.append(" + ");
}
}
sb.append(")");
return sb.toString();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((terms == null) ? 0 : terms.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Disjunction other = (Disjunction) obj;
if (terms == null)
{
if (other.terms != null)
return false;
}
else if (!terms.equals(other.terms))
return false;
return true;
}
}
static class Conjunction
{
private final Set<Expression> terms;
Conjunction(Collection<? extends Expression> expressions)
{
this.terms = new LinkedHashSet<Expression>(expressions);
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("(");
int n = 0;
for (Expression e : terms)
{
sb.append(e);
n++;
if (n < terms.size())
{
sb.append(" * ");
}
}
sb.append(")");
return sb.toString();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((terms == null) ? 0 : terms.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Conjunction other = (Conjunction) obj;
if (terms == null)
{
if (other.terms != null)
return false;
}
else if (!terms.equals(other.terms))
return false;
return true;
}
}
private static class Rule
{
Conjunction conjunction;
String action;
@SafeVarargs
Rule(String action, Disjunction ... disjunctions)
{
this.action = action;
this.conjunction = new Conjunction(Arrays.asList(disjunctions));
}
Rule(String action, Conjunction conjunction)
{
this.action = action;
this.conjunction = conjunction;
}
@Override
public String toString()
{
return conjunction+" -> "+action;
}
}
}
答案 1 :(得分:1)
一种可能性是使用binary decision diagrams。这些允许有效操纵布尔公式。正如其他人所提到的,您可以将规则视为二进制公式:规则1是(r OR(w AND g)OR l),规则2是(r AND a AND l),并决定规则4是否冗余,我们需要检查(Rule4 AND(NOT(Rule1 OR Rule2 OR Rule3)))是否有解决方案。此外,需要使用像(NOT(w和g))这样的条款来禁止无效输入。有很多可用的求解器,虽然无法保证运行时间或内存使用量不会爆炸,但它们通常可以很好地用于实际输入。
答案 2 :(得分:1)
您的每个规则都代表一个布尔条件。目前还不清楚你的规则符号是多么富有表现力(我可以说,&#34;不是Wood&#34;?),但规则1似乎是
Color==Red and (Material==Wood or Material==Glass) and Size==Large
您可能还有一些&#34;域约束&#34;,C(k),可能是以下形式:
Color==Red ==> Not(Color==Green)
其中==&gt;是逻辑蕴涵算子。
你似乎想要大致知道的是,对于某些我和j,R(i)意思是&#34; ith规则&#34;:
R(i) ==> R(j)
在实践中,您需要知道:
R(i) and C(1) and C(2) and ... C(K) ==> R(j)
你可以(并且必须)通过基本上做布尔代数来解决这个问题。 这实际上相对简单,例如,将整个方程(包括蕴涵算子)视为实际的符号公式并应用布尔代数定律。
让我们以代数方式运作你的榜样(长篇)
首先,对于您的示例规则系统,域约束C(i)是:
Color==Red ==> Not(Color == Green)
Color==Red ==> Not(Color == Blue)
Color==Blue ==> Not(Color == Green)
Color==Blue ==> Not(Color == Red)
Color==Green ==> Not(Color == Red)
Color==Green ==> Not(Color == Blue)
(Color==Red or Color==Blue or Color===Green) <==> true [if only these 3 colors exist]
和类似材料(木材,玻璃,铝)和尺寸(小,中,大)。
对于您的具体示例,R(1)是:
Color==Red and (Material==Wood or Material==Glass) and Size==Large
和R(4)是:
Color==Red and (true) and Size==Large
您想知道的是:
R(1) and <domainconstraints> ==> R(4)
这是一个非常庞大的公式,但这对计算机来说真的只是一个问题。在这种特殊情况下,我们不需要域限制(我,oracle,说话),所以我只是将其排除在外以摆脱巨大的限制:
R(1) and true ==> R(4)
或只是
R(1) ==> R(4)
(实施提示:在尝试R(i)和==&gt; R(j)之前,你总是先尝试R(i)==&gt; R(j),因为第一个意味着第二个。这可以是如果检测到冗余,则用于保持术语的大小。
&#34; a ==&gt; B&#34; operator是布尔等价于&#34; ~a或b&#34;所以你想知道这个公式是否为真:
Not(R(1)) or R(4)
插入R(1)和R(4)的定义:
Not(Color==Red and (Material==Wood or Material==Glass) and Size==Large) or (Color==Red and (true) and Size==Large)
使用DeMorgan的法律并简化&#34;和(true)&#34;:
Not(Color==Red) or Not( (Material==Wood or Material==Glass) ) or Not( Size==Large) or Color==Red and Size==Large
第二次申请DeMorgan的法律(我们不需要这样做,因为Redness将会推动答案,但我们还不应该知道这一点):
Not(Color==Red) or Not(Material==Wood) and Not(Material==Glass) or Not(Size==Large) or Color==Red and Size==Large
事实:
a and b ==> a
所以,对于任何b,
a and b or a == a
使用not not(颜色==红色),b为Size == Large:
Not(Color==Red) and Size==Large or Not(Color==Red) or or Not(Material==Wood) and Not(Material==Glass) or Not(Size==Large) or Color==Red and Size==Large
现在我们将条款分组:
Not(Color==Red) and Size==Large or Color==Red and Size==Large or Not(Color==Red) or or Not(Material==Wood) and Not(Material==Glass) or Not(Size==Large)
组合具有尺寸==大的术语:
( Not(Color==Red) or Color==Red) and Size==Large Not(Color==Red) or Not(Material==Wood) and Not(Material==Glass) or Not(Size==Large)
使用a或~a == true:
( true ) and Size==Large or Not(Color==Red) or Not(Material==Wood) and Not(Material==Glass) or Not(Size==Large)
简化和分组大小术语:
(Size==Large or Not(Size==Large)) or Not(Color==Red) or Not(Material==Wood) and Not(Material==Glass)
给予(哇)
(true) or ...
产生真,所以R(1)==&gt; R(4)因而R(4)是多余的。
实施(或TL; DR)
显然你不想手工做这件事。您可以将公式编码为布尔表达式(您已经完成了类似的操作,以便能够将它们存储在您的系统中)。你想知道的是,整个声明是否是一个重言式;为此,您可以使用Wang's rules of inference。这实施起来有点笨拙但可行。
二进制决策图(糟糕的名称)是一种编码&#34;真值表的方法&#34;一个公式。你想要的是决定一个公式的真值表是否总是正确的。正如另一张海报所说,你可以获得BDD包。如果你为这个蕴涵方程式构建BDD,它就是&#34; TRUE&#34;无处不在(也就是说,你获得了一个微不足道的BDD),然后暗示是真的,你有一个冗余。
检查这个的运行时间可能会很昂贵。您可以承担这笔费用,或者只是设置每次尝试将给予多少CPU的上限;或者它产生答案&#34;没有冲突&#34;,&#34;冲突&#34;或&#34;超时&#34;,&#34;超时&#34;被解释为&#34;没有冲突&#34;。然后,您可以根据需要快速运行,模数有时不会消除冗余规则。
由于您的规则是独立声明,如果您有N条规则,则您需要大约N ^ 2次进行此测试。 (好消息是,如果你已经建立了一个N规则的可信数据库,添加新规则只需要N次测试。)
答案 3 :(得分:0)
首先,让我们看一下您的示例,而不是**ANY**
我们放置所有选项
+---------------------------------++--------------
| Rule Parameters || Rule Action
+----------+-----------+----------++--------------
| Color | Material | Size || Price
+----------+-----------+----------++--------------
Rule 1 | R | W,G | L || $100
Rule 2 | R | A | L || $200
Rule 3 | B,G | W | M || $300
Rule 4 | R | A,W,G | L || $400
Rule 5 | R,B,G | A,W,G | S,M,L || $500
接下来,我们需要定义规则如何冗余,所以这里是我的定义
如果规则等于除其自身之外的任何k规则的合并,则该规则是多余的。
因此,基于此定义(注意1 <= k < n
其中n
是规则数),我们可以看到找到所有冗余规则会花费大量时间。
因此,如果我们的规则集中有n = 100 rules
,则此强力攻击将使n^3 * n!
为Time ~ 100^3 * (1! + 2! + .. + 100!) = 10^6 * 9.4 * 10^158 = 9.333 * 10^164
。
这是强力检查的版本,需要O(n^3 * SUM { k! | k = 1..n } )
:
boolean isRedundant (RuleList R, Rule d)
{
// R is the set of rules
// T is a copy of the set, without the rule to be checked
// d is the rule to be checked
T = new RuleList(R);
T.remove(d);
for (int i=0; i<T.size(); i++) // check single rules
if (T.get(j).equal(d))
return true;
for (int k=1; k<R.size(); k++) // 1 <= k < n
{
// merge every k-permutation to check
for (RuleList permutation : T.getPermutations(k))
{
Rule r = new Rule ();
for (Rule p : permutation)
r.merge(p);
if (r.equal(d))
return true;
}
}
return false;
}
班级规则
class Rule
{
public boolean [3] color; // R,G,B
public boolean [3] material; // A,W,G
public boolean [3] size; // S,M,L
public Rule ()
{
color = { false, false, false };
material = { false, false, false };
size = { false, false, false };
}
public void merge (Rule r)
{
color[0] = and(color[0], r.color[0]);
color[1] = and(color[1], r.color[1]);
color[2] = and(color[2], r.color[2]);
material[0] = and(material[0], r.material[0]);
material[1] = and(material[1], r.material[1]);
material[2] = and(material[2], r.material[2]);
size[0] = and(size[0], r.size[0]);
size[1] = and(size[1], r.size[1]);
size[2] = and(size[2], r.size[2]);
}
public boolean equal (Rule r)
{
return (color[0]==r.color[0]
&& color[1]==r.color[1]
&& color[2]==r.color[2]
&& material[0]==r.material[0]
&& material[1]==r.material[1]
&& material[2]==r.material[2]
&& size[0]==r.size[0]
&& size[1]==r.size[1]
&& size[2]==r.size[2]);
}
public boolean and(boolean a, boolean b)
{
return (a && b);
}
}
替代解决方案
让我们说我们有代表每个参数的集合,然后我们可以从规则总数减少要检查的规则数量,这也可以优化时间复杂度,因为我们将所有成员视为一个来自那个观点,然后通过一个简单的匹配算法。
+---------------------------------++--------------
| Rule Parameters || Rule Action
+----------+-----------+----------++--------------
| Color | Material | Size || Price
+----------+-----------+----------++--------------
Rule 1 | R | W,G | L || $100
Rule 2 | R | A | L || $200
Rule 3 | B,G | W | M || $300
Rule 4 | R | A,W,G | L || $400
Rule 5 | R,B,G | A,W,G | S,M,L || $500
我们有以下内容:
Color_R = { 1, 2, 4, 5 }
Color_B = { 3, 5 }
Color_G = { 3, 5 }
Material_A = { 2, 4, 5 }
Material_W = { 1, 3, 4, 5 }
Material_G = { 1, 4, 5 }
Size_S = { 5 }
Size_M = { 3, 5 }
Size_L = { 1, 2, 4, 5 }
现在,对于每个规则,我们采用其参数集,然后匹配回来而不查看相同的规则..
示例:
Rule_4 = { Color_R, Material_A, Material_W, Material_G, Size_L }
Color_R = { 1, 2, x, 5 }
Material_A = { 2, x, 5 }
Material_W = { 1, 3, x, 5 }
Material_G = { 1, x, 5 }
Size_L = { 1, 2, x, 5 }
我们检查与所有会员资格匹配的规则集:
Matched = { 5, 1+2 }
然后我们将它们与所选规则进行比较,通过set-difference寻找phi:
Rule_5 - Rule_4 = { Color_B, Color_G, Size_S, Size_M }
> Rule_5 does not match
(Rule_1 + Rule2) - Rule_4 = { }
> (Rule_1 + Rule2) match
最后,我们得出结论:
(Rule_1 + Rule2) = Rule_4
> Rule_4 is redundant
答案 4 :(得分:0)
在强类型函数式语言中,pattern matching领域有大量工作。关于它实际上是什么的问题甚至被讨论过here, on stackoverflow。
Luc Maranget的publication page在各种设置中包含许多关于该主题的文章,其中一些作为OCaml模式匹配算法的基础。
他关于compiling pattern matching to good decision trees的论文可能是你问题灵感的确切来源。它还提供了有关同一主题的先前文章的参考。基本原则是将模式匹配集描述为整数矩阵,以及代数运算。
答案 5 :(得分:0)
此问题中的规则数量非常大,但值的数量有限。这就是为什么我认为规则评估规则不会给出最快的结果。 如果规则按值分组,则可以一起评估多个规则。 我提出的算法仍然可以爆炸,但我希望不太可能这样做。
示例数据:
Rule #: [Color (R,G,B),Material (W,G,A),Size (S,M,L)]
Rule 1: [[1,0,0],[1,1,0],[0,1,0]]
Rule 2: [[1,0,0],[0,0,1],[0,1,0]]
Rule 3: [[0,1,1],[1,0,0],[0,0,1]]
Rule 4: [[1,0,0],[1,1,1],[0,1,0]]
Rule 5: [[1,1,1],[1,1,1],[1,1,1]]
(复制自核武器)
步骤1:按参数1的值拆分规则
将Set中的规则分组为参数1的值。 可能需要启发式来选择最佳的第一个参数。 现在我只是按顺序挑选它们。
Result:
Rules in Set R: 1,2,4,5
Rules in Set G: 3,5
Rules in Set B: 3,5
第2步:合并等分集
Set G en B在此级别包含相同的规则,因此可以加入。
Result:
Rules in Set R: 1,2,4,5
Rules in Set GB: 3,5
步骤3:按参数2的值拆分组
对于所有设置,规则现在检查参数2。 对于每个不同的值,该集合被分成子集。 第2步的集合不再相关。
Result:
Rules in Set (R ,W): 1,4,5
Rules in Set (R ,G): 1,4,5
Rules in Set (R ,A): 2,4,5
Rules in Set (GB,W): 3,5
Rules in Set (GB,G): 5
Rules in Set (GB,A): 5
第4步:删除单一规则
现在证明规则5是相关的。它单独捕获(G,G,*),(B,G,*),(G,A,*)和(B,A,*)。 可以从评估中删除Set(GB,G)和(GB,A),因为它们无法再证明了。
第5步:合并等分集
Result:
Rules in Set (R ,WG): 1,4,5
Rules in Set (R ,A ): 2,4,5
Rules in Set (GB,W ): 3,5
步骤3:按参数3的值拆分组
Result:
Rules in Set (R ,WG, S): 5
Rules in Set (R ,WG, M): 1,4,5
Rules in Set (R ,WG, L): 5
Rules in Set (R ,A ,S): 5
Rules in Set (R ,A ,M): 2,4,5
Rules in Set (R ,A ,L): 5
Rules in Set (GB,W ,S): 5
Rules in Set (GB,W ,M):5
Rules in Set (GB,W ,L): 3,5
规则5被证明是有用的,没有计算规则序列。 1,2,3被证明,因为Set中有最高优先级。规则4永远不会被证明并且是多余的。
现在使用伪代码:
void getProven(RuleList rulelist, RuleList provenlist, ParameterIterator i)
{
ValuesToRulelistMap subsets = new ValuesToRulelistMap();
Parameter parameter = i.next();
for (Rule rule : rulelist) {
Valuelist valuelist = rule.getValuesByParameter(parameter);
for (Value value : valueslist) {
subsets.addToListInMap(value, rule);
}
KeyList doneKeys = new KeyList();
for (RuleList subset : subsets.getRuleLists()) {
if (subset.length == 0) {
// No rule for this case!!!
} else
if (subset.length == 1) {
// Singly proven
provenlist.add(subset.getFirst());
} else
if (!i.hasNext()) {
// Proven by priority
provenlist.add(subset.getFirst());
} else
Key key = subset.combineRulesInListToKey()
if (!doneKeys.containts(key)) {
getProven(subset, provenlist, i);
doneKeys.add(key);
}
}
}
}
}