如何获得 Drools 中列表的所有可能组合?
How can I get all possible combinations of a list in Drools?
我有一个可变大小的列表,我想获得所有可能的组合。给定一个包含 {"A","B","C"} 的列表,我正在寻找以下输出(以任何顺序):
A
AB
ABC
AC
B
BC
C
就像this js answer
我可以轻松得到预定义数字的所有排列,如下所示:
rule "Permutation of 3 List" extends "My List"
when
$obj1: MyClass() from $myList
$obj2: MyClass() from $myList
$obj3: MyClass() from $myList
then
List<MyClass> list = new ArrayList<MyClass>();
list.add($obj1);
list.add($obj2);
list.add($obj3);
insert(list);
结束
我不确定如何获得可变数字的排列,这可能对我需要组合没有帮助,但如果我可以获得 n 个大小为 k 的元素的所有排列,我可以 运行 大小为 1 到大小为 n 的 n 个元素的所有排列,然后从这些排列中取不同的值。但是,我还需要它相对高效,并且没有包含 20 个元素的列表会使服务器崩溃。
编辑:修复了 js 答案 link
编辑:删除添加的答案并发布为答案
假设您有一组 n 个元素 S = {"A", "B", "C", ... }。然后你可以构造一个大小为 n 的数组 X,其中 X[i] 表示第 i 个元素是否在排列中。
例如:S = {"A", "B", "C"} 以 X =[0,0,0]
开头
- X = [0,0,1]代表"C"
- X = [0,1,0] 表示 "B"
- X = [0,1,1]代表"BC"
- X = [1,0,0]代表"A"
- X = [1,0,1]代表"AC"
- X = [1,1,0]代表"AB"
- X = [1,1,1]代表"ABC"
现在,对于 X 与一个以上“1”的每个组合,您可以使用当前算法找到所有排列。
即:对于 X = [1,1,0] 你将不得不输出 "AB" 和 "BA",但是对于 X = [1,1,1] 你将不得不输出"ABC"、"ACB"、"BCA"、"BAC"、"CBA" 和 "CAB".
我想出了一个可能效率不高但有效的方法。
我做的第一件事是创建一个名为 Data
的简单 Java class 来包含字符串的初始列表(我不喜欢 JDK classes 作为事实)。
public class Data {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
然后,我使用一个中间事实在我的 DRL 中保存一个排列结果。
declare Permutation
elements : List
end
然后,我创建了一个规则来为初始 data
.
创建所有可能的排列(具有可变元素)
rule "Permutations"
when
$d: Data()
$obj1: String() from $d.getList() do[one]
$obj2: String(this != $obj1) from $d.getList() do[two]
$obj3: String((this != $obj1 && this != $obj2)) from $d.getList()
then
List<String> list = Arrays.asList($obj1, $obj2, $obj3);
insert(new Permutation(list));
then[one]
List<String> list = Arrays.asList($obj1);
insert(new Permutation(list));
then[two]
List<String> list = Arrays.asList($obj1, $obj2);
insert(new Permutation(list));
end
上面的规则使用 Conditional Named Consequences 来避免必须创建 3 个不同的规则。
该规则还使用 this != $objX
类型的约束来避免重复元素的排列。然后每个 Permutation
作为一个独立的事实插入。
最后一步是从会话中删除 Permutations
具有相同元素的内容。为此,我使用了 List.containsAll() 方法。
rule "Trim duplicated Permutations"
when
$p1: Permutation()
$p2: Permutation(
this != $p1,
elements.size == $p1.elements.size,
elements.containsAll($p1.elements)
)
then
retract($p2);
end
就是这样!为了从您的 Java 应用程序中获取会话中剩余的排列,我使用了一个查询:
query getPermutations()
Permutation($p: elements)
end
我用来测试的 Java 代码是:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Data data = new Data();
data.setList(list);
ksession.insert(data);
ksession.fireAllRules();
QueryResults results = ksession.getQueryResults("getPermutations");
for (QueryResultsRow row : results) {
List<String> permutation = (List<String>)row.get("$p");
String m = "Permutation: {"+permutation.stream().collect(Collectors.joining(","))+"}";
System.out.println(m);
}
希望对您有所帮助,
我想出了一种方法来获取 Drools 中 n 项列表的所有组合,但我不会说这是 Drools 的方式。这是我的规则,它获取我的列表并从列表中获取所有组合:
rule "Combinations of List" extends "My List"
salience 100
when
then
//bit shift left j places, equivalent to Math.pow(2,j);
int max = 1 << $myList.size();
for(int i = 1; i < max; i++){
insert(new ComboNumber(i));
}
end
这个想法来自@FlyingPumba 评论和 dba answer here
int 只是位,对于所有组合,我需要位 0b001-0b111(1-7 的二进制)以及它们之间的所有组合。然后 001 是 "A",111 是 "ABC",101 是 "AC",等等......我从 (2^n)-1 得到它,在这种情况下是 (2^3)- 1,即 7,然后遍历所有值 [1,2,3,4,5,6,7],为每个值插入一个新的 ComboNumber 到工作内存。
然后对于这些 ComboNumber 中的每一个,我需要从该数字的位构造 MyClass 的组合:
rule "Construct Combination From Bits" extends "My List"
salience 100
when
$list: List()
ComboNumber($number: number) from $list
then
List comboList = new ArrayList();
for (int bit = 0; bit < $myList.size(); bit++)
{
//if current bit position is flipped on
//bitshift right of 0b001101 2 places is 0b0011
//logical AND of (0b0011 and 0b0001) = 0b0001 (or decimal 1)
if((($number >> bit) & 1) == 1){
//get obj associated with bit position
comboList.add($myList.get(bit));
}
}
insert(new Combinations(comboList));
end
这种方式可行,但...在性能上不是很好。毕竟是 O(2^n)。我相信获得所有排列是 O(n^n),所以它可能更糟。包含 17 个或更少项目的列表是亚秒级的,但 20 个需要 23 秒(即 1,048,576 种组合)。
如果你能想出更好的性能方法,请告诉我。 运行 它直接在 C# 中(在我的同一台机器上)大约快 23 倍(单线程)或并行快 46 倍,每秒成功迭代约 2m 组合。
我有一个可变大小的列表,我想获得所有可能的组合。给定一个包含 {"A","B","C"} 的列表,我正在寻找以下输出(以任何顺序):
A
AB
ABC
AC
B
BC
C
就像this js answer
我可以轻松得到预定义数字的所有排列,如下所示:
rule "Permutation of 3 List" extends "My List"
when
$obj1: MyClass() from $myList
$obj2: MyClass() from $myList
$obj3: MyClass() from $myList
then
List<MyClass> list = new ArrayList<MyClass>();
list.add($obj1);
list.add($obj2);
list.add($obj3);
insert(list);
结束
我不确定如何获得可变数字的排列,这可能对我需要组合没有帮助,但如果我可以获得 n 个大小为 k 的元素的所有排列,我可以 运行 大小为 1 到大小为 n 的 n 个元素的所有排列,然后从这些排列中取不同的值。但是,我还需要它相对高效,并且没有包含 20 个元素的列表会使服务器崩溃。
编辑:修复了 js 答案 link
编辑:删除添加的答案并发布为答案
假设您有一组 n 个元素 S = {"A", "B", "C", ... }。然后你可以构造一个大小为 n 的数组 X,其中 X[i] 表示第 i 个元素是否在排列中。
例如:S = {"A", "B", "C"} 以 X =[0,0,0]
开头- X = [0,0,1]代表"C"
- X = [0,1,0] 表示 "B"
- X = [0,1,1]代表"BC"
- X = [1,0,0]代表"A"
- X = [1,0,1]代表"AC"
- X = [1,1,0]代表"AB"
- X = [1,1,1]代表"ABC"
现在,对于 X 与一个以上“1”的每个组合,您可以使用当前算法找到所有排列。
即:对于 X = [1,1,0] 你将不得不输出 "AB" 和 "BA",但是对于 X = [1,1,1] 你将不得不输出"ABC"、"ACB"、"BCA"、"BAC"、"CBA" 和 "CAB".
我想出了一个可能效率不高但有效的方法。
我做的第一件事是创建一个名为 Data
的简单 Java class 来包含字符串的初始列表(我不喜欢 JDK classes 作为事实)。
public class Data {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
然后,我使用一个中间事实在我的 DRL 中保存一个排列结果。
declare Permutation
elements : List
end
然后,我创建了一个规则来为初始 data
.
rule "Permutations"
when
$d: Data()
$obj1: String() from $d.getList() do[one]
$obj2: String(this != $obj1) from $d.getList() do[two]
$obj3: String((this != $obj1 && this != $obj2)) from $d.getList()
then
List<String> list = Arrays.asList($obj1, $obj2, $obj3);
insert(new Permutation(list));
then[one]
List<String> list = Arrays.asList($obj1);
insert(new Permutation(list));
then[two]
List<String> list = Arrays.asList($obj1, $obj2);
insert(new Permutation(list));
end
上面的规则使用 Conditional Named Consequences 来避免必须创建 3 个不同的规则。
该规则还使用 this != $objX
类型的约束来避免重复元素的排列。然后每个 Permutation
作为一个独立的事实插入。
最后一步是从会话中删除 Permutations
具有相同元素的内容。为此,我使用了 List.containsAll() 方法。
rule "Trim duplicated Permutations"
when
$p1: Permutation()
$p2: Permutation(
this != $p1,
elements.size == $p1.elements.size,
elements.containsAll($p1.elements)
)
then
retract($p2);
end
就是这样!为了从您的 Java 应用程序中获取会话中剩余的排列,我使用了一个查询:
query getPermutations()
Permutation($p: elements)
end
我用来测试的 Java 代码是:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Data data = new Data();
data.setList(list);
ksession.insert(data);
ksession.fireAllRules();
QueryResults results = ksession.getQueryResults("getPermutations");
for (QueryResultsRow row : results) {
List<String> permutation = (List<String>)row.get("$p");
String m = "Permutation: {"+permutation.stream().collect(Collectors.joining(","))+"}";
System.out.println(m);
}
希望对您有所帮助,
我想出了一种方法来获取 Drools 中 n 项列表的所有组合,但我不会说这是 Drools 的方式。这是我的规则,它获取我的列表并从列表中获取所有组合:
rule "Combinations of List" extends "My List"
salience 100
when
then
//bit shift left j places, equivalent to Math.pow(2,j);
int max = 1 << $myList.size();
for(int i = 1; i < max; i++){
insert(new ComboNumber(i));
}
end
这个想法来自@FlyingPumba 评论和 dba answer here
int 只是位,对于所有组合,我需要位 0b001-0b111(1-7 的二进制)以及它们之间的所有组合。然后 001 是 "A",111 是 "ABC",101 是 "AC",等等......我从 (2^n)-1 得到它,在这种情况下是 (2^3)- 1,即 7,然后遍历所有值 [1,2,3,4,5,6,7],为每个值插入一个新的 ComboNumber 到工作内存。
然后对于这些 ComboNumber 中的每一个,我需要从该数字的位构造 MyClass 的组合:
rule "Construct Combination From Bits" extends "My List"
salience 100
when
$list: List()
ComboNumber($number: number) from $list
then
List comboList = new ArrayList();
for (int bit = 0; bit < $myList.size(); bit++)
{
//if current bit position is flipped on
//bitshift right of 0b001101 2 places is 0b0011
//logical AND of (0b0011 and 0b0001) = 0b0001 (or decimal 1)
if((($number >> bit) & 1) == 1){
//get obj associated with bit position
comboList.add($myList.get(bit));
}
}
insert(new Combinations(comboList));
end
这种方式可行,但...在性能上不是很好。毕竟是 O(2^n)。我相信获得所有排列是 O(n^n),所以它可能更糟。包含 17 个或更少项目的列表是亚秒级的,但 20 个需要 23 秒(即 1,048,576 种组合)。
如果你能想出更好的性能方法,请告诉我。 运行 它直接在 C# 中(在我的同一台机器上)大约快 23 倍(单线程)或并行快 46 倍,每秒成功迭代约 2m 组合。