我有一个可变大小的列表,我希望得到所有可能的组合。如果列表包含{" A"," B"," C"}我正在寻找以下输出(按任意顺序):
A
AB
ABC
AC
B
BC
C
一样
我可以轻松获得预定义数字的所有排列,如下所示:
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);
端
我不确定如何获取变量数的排列,并且它可能没有帮助,因为我需要组合,但如果我可以获得大小为k的n个元素的所有排列,我可以运行所有排列对于n个元素大小为1,大小为n,然后从这些排列中获取不同的值。但是,我还需要相对高效,并且没有包含20个元素的列表使服务器崩溃。
编辑:修复js回答链接
修改:删除已添加的答案并发布为答案
答案 0 :(得分:1)
我们假设您有一组n个元素S = {&#34; A&#34;,&#34; B&#34;,&#34; C&#34;,...} 。然后你可以构造一个大小为n的数组X,其中X [i]表示如果第i个元素在排列中。
例如:S = {&#34; A&#34;,&#34; B&#34;,&#34; C&#34;}并以X = [0,0,0]开头
现在,对于X与多个&#34; 1&#34;的每个组合您可以使用当前算法查找所有排列。
即:对于X = [1,1,0],您必须输出&#34; AB&#34;和&#34; BA&#34;,但是对于X = [1,1,1],你必须输出&#34; ABC&#34;,&#34; ACB&#34;,&#34; BCA&# 34;,&#34; BAC&#34;,&#34; CBA&#34;和&#34; CAB&#34;。
答案 1 :(得分:1)
我提出了一种可能效率不高但行之有效的方法。
我做的第一件事就是创建一个名为Data
的简单Java类来包含字符串的初始列表(我不喜欢JDK类作为事实)。
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);
}
希望它有所帮助,
答案 2 :(得分:0)
我想出了一种方法来获取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倍,成功迭代每秒大约2米组合。