如何在Drools中获得列表的所有可能组合?

时间:2015-12-17 02:08:17

标签: algorithm 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);

我不确定如何获取变量数的排列,并且它可能没有帮助,因为我需要组合,但如果我可以获得大小为k的n个元素的所有排列,我可以运行所有排列对于n个元素大小为1,大小为n,然后从这些排列中获取不同的值。但是,我还需要相对高效,并且没有包含20个元素的列表使服务器崩溃。

编辑:修复js回答链接
修改:删除已添加的答案并发布为答案

3 个答案:

答案 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 = [0,0,1]表示&#34; C&#34;
  • X = [0,1,0]表示&#34; B&#34;
  • X = [0,1,1]表示&#34; BC&#34;
  • X = [1,0,0]表示&#34; A&#34;
  • X = [1,0,1]表示&#34; AC&#34;
  • X = [1,1,0]表示&#34; AB&#34;
  • X = [1,1,1]表示&#34; ABC&#34;

现在,对于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米组合。