从多个列表中查找元素中的(100)最高总和

时间:2012-11-12 12:33:37

标签: algorithm combinations

我有以下问题(在我的版本中有一些限制可能会使问题更容易解决,但一般解决方案也会很好):

我有4个列表,有10个条目。第一个列表包含0到6之间的整数条目,其他三个包含0到3之间的条目。 我现在必须找到这四个列表的100个最佳元素组合。 一个组合由4个值的总和组成,每个列表中有一个值。 请注意,我不仅需要知道结果元素的值,还需要知道它们的组成方式,因为有更多与值相关的信息。

示例1:

list A: 6, 3, 2, 2, 1, 0, 0, 0, 0
list B: 3, 2, 0, 0, 0, 0, 0, 0, 0
list C: 2, 2, 0, 0, 0, 0, 0, 0, 0
list D: 3, 1, 1, 1, 1, 1, 0, 0, 0

在这种情况下,五种最佳组合是:

  • 14(A [0] + B [0] + C [0] + D [0])
  • 14(A [0] + B [0] + C [1] + D [0])
  • 13(A [0] + B [1] + C [0] + D [0])
  • 13(A [0] + B [1] + C [1] + D [0])
  • 12(A [0] + B [0] + C [0] + D [1])

请注意,我已经对列表的条目进行了排序,因为这可能是解决此问题的大多数算法的第一步。

普通解决方案

最简单的解决方案包括形成所有10.000种可能的组合,然后选择其中的百种组合。 人们甚至不需要对10.000种可能的组合进行排序。 可以先扫描阵列并分析出现的频率。 然后,可以在下一次扫描值中选择一百个最佳值(及其进一步的属性)。

不起作用的解决方案

我想到的另一个想法如下: 首先必须对列表进行排序。 在每个列表中,我想找到一个索引,它将那些可以为它们提供解决方案的条目分开,而这些条目不能。 当必须在示例1中找到四个最佳组合时,可以例如选择列表BC的前两个元素,以及列表A和{{ 1}} 这会产生:

D

这些子列表的所有组合都将产生最佳解决方案。 但是,这并不总是可行的,如以下示例所示:

示例2:

(这次只有两个清单)

A: 6
B: 3, 2
C: 3, 2
D: 3

这里,最好的四个解决方案是

  • 6 + 3
  • 5 + 3
  • 5 + 3
  • 6 + 1

然而,使用索引无法找到此解决方案,索引将四种组合与所有其他组合分开。

5 个答案:

答案 0 :(得分:3)

让我试着用更简洁的方式重新解释现有的答案(由Evgeny Kluev,solendil)。

解决方案是从配置(0,0,0,0)开始的Best-first search。 搜索树的分支因子为4(列表数)。

在(0,0,0,0)你知道下一个最好的配置是(1,0,0,0),(0,1,0,0),(0,0,1)之一,0)或(0,0,0,1)。 因此,您将搜索树的这些“叶子”放入优先级队列,并按每个配置的好坏排序。 叶子队列是下一个最佳配置候选者的队列。

然后从队列中取出最佳配置以添加到答案列表中 并更新队列。 例如,如果(0,0,1,0)是下一个最佳的,则将其从队列中取出 然后将其子项 - (1,0,1,0),(0,1,1,0),(0,0,2,0),(0,0,1,1) - 添加到队列中。< / p>

答案 1 :(得分:2)

此解决方案使用两种数据结构:

  1. 优先级队列,其中列表元素的总和是关键,列表索引的元组是值。
  2. 包含列表索引元组的哈希集。
  3. 算法:

    1. 对每个列表进行排序。
    2. 将包含每个列表的第一个元素的元组放入队列中。
    3. 虽然哈希集包含少于100个元组,但请执行步骤4和步骤5。
    4. 从队列中弹出最大的项目,检查哈希集是否包含对应的索引元组(T)。如果找到这样的元组,请重复步骤4,否则继续执行步骤5.
    5. 将元组T插入哈希集。创建N个元组(其中N是列表数),每个元组的N-1索引等于T中的对应索引,一个索引大于1,并将这些元组插入队列。
    6. 实现优先级队列(针对此问题)的最佳方法可能是使用有序容器(二进制搜索树或跳过列表),首先按列表元素的总和排序,然后按列表索引排序。这使得不需要单独的哈希集,因为这个容器可以在将重复项添加到队列之前检测它们。

答案 2 :(得分:2)

Prereqs for my answer:解决方案表示为4位数字。 0000(14)表示解决方案,其中您获取每个列表的第一个元素,得分为14. 1052(7)取第一个列表中的第二个,第二个,第三个中的第六个和第四个中的第三个,得分为7。

现在,您构建一个以0000(14)开头的树,并通过将四个数字中的每一个递增1来找到所有叶子,即1000(11),0100(13),0010(14)和0001(12)。你的最佳总和是14,0010(14)成为一个分支(是解决方案的一部分),你计算它的叶子,即1010(11),0110(13),0020(12)和0011(12)。所有叶子的最佳总和是13,你将这些叶子设置为分支,计算它们的叶子等,直到你已经增长了100个解决方案。

此方法以20个步骤解决您的示例问题,计算102个分支和188个叶子。

这是一个解决您问题的(粗略)Java程序。

package HighestSums;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class HighestSums {

static int[][] data = {
                {6, 3, 2, 2, 1, 0, 0, 0, 0, 0},
                {3, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {2, 2, 0, 0, 0, 0, 0, 0, 0, 0},
                {3, 1, 1, 1, 1, 1, 0, 0, 0, 0}
};

static Set<Solution> branches = new HashSet<Solution>();
static Set<Solution> leaves = new HashSet<Solution>();

public static void main(String[] args) {
    Solution s = new Solution();
    leaves.add(s);
    int sum = s.getSum();

    for (int i=0; i<100; i++) {
        System.out.println("======== STEP " + i);
        System.out.println("-- Nb Branches : " + branches.size());
        System.out.println("-- Nb Leaves : " + leaves.size());
        if (branches.size()>100)
            return;
        sum = max(leaves);
        step(sum);
        System.out.println("Sum : " + sum);
        System.out.println("Res\n" + toStr(branches));
        System.out.println("Analyse\n" + toStr(leaves));
    }
}

private static int max(Set<Solution> analyse2) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(analyse2);
    Collections.sort(disp);
    return disp.get(0).getSum();
}

private static String toStr(Collection<Solution> l) {
    List<Solution> disp = new ArrayList<HighestSums.Solution>();
    disp.addAll(l);
    Collections.sort(disp);
    String res = "";
    for (Solution s : disp)
        res += s.toString()+"\n";
    return res;
}

private static void step(int sum) {
    List<Solution> created = new ArrayList<Solution>();
    for (Iterator<Solution> it = leaves.iterator(); it.hasNext(); ) {
        Solution a = it.next();
        if (a.getSum()==sum) {
            it.remove();
            branches.add(a);
            for (Solution a2 : a.next()) {
                if (branches.contains(a2) || leaves.contains(a2))
                    continue;
                created.add(a2);
            }
        }
    }
    leaves.addAll(created);
}

static class Solution implements Comparable<Solution>{
    int[] ix = new int[4];
    Solution parent;
    public Solution(Solution sol) {
        System.arraycopy(sol.ix, 0, ix, 0, sol.ix.length);
        parent = sol;
    }
    public Solution() {}
    public String toString() {
        String res = "";
        for (int i=0; i<4; i++) 
            res += ix[i]; 
        res += " " + getSum(); 
        if (parent != null) 
            res += " (" + parent.toString() + ")";
        return res;
    }
    public List<Solution> next() {
        List<Solution> res = new ArrayList<Solution>();
        for (int i=0; i<4; i++) {
            if (ix[i]<9) {
                Solution s2 = new Solution(this);
                s2.ix[i]+=1;
                res.add(s2);
            }
        }
        return res;
    }
    private int getSum() {
        int res = 0;
        for (int i=0; i<4; i++) 
            res += data[i][ix[i]]; 
        return res;
    }
    @Override
    public boolean equals(Object obj) {
        Solution s = (Solution)obj;
        for (int i=0; i<4; i++) 
            if (ix[i]!=s.ix[i])
                return false;
        return true;
    }
    @Override
    public int hashCode() {
        return ix[0]+ix[1]*10+ix[2]*100+ix[3]*1000;
    }
    @Override
    public int compareTo(Solution o) {
        return o.getSum() - getSum();
    }
}

}

现在的解决方案(所有102次出现):

0000 14
0010 14 (0000 14)
0100 13 (0000 14)
0110 13 (0010 14 (0000 14))
0011 12 (0010 14 (0000 14))
0003 12 (0002 12 (0001 12 (0000 14)))
0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0030 12 (0020 12 (0010 14 (0000 14)))
0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))
0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))
0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0002 12 (0001 12 (0000 14))
0012 12 (0011 12 (0010 14 (0000 14)))
0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))
0020 12 (0010 14 (0000 14))
0001 12 (0000 14)
0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
1000 11 (0000 14)
0200 11 (0100 13 (0000 14))
0111 11 (0110 13 (0010 14 (0000 14)))
0180 11 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0300 11 (0200 11 (0100 13 (0000 14)))
0130 11 (0030 12 (0020 12 (0010 14 (0000 14))))
0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))
0114 11 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))
0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))
0160 11 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))
0104 11 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))
0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14))))))))
0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0900 11 (0800 11 (0700 11 (0600 11 (0500 11 (0400 11 (0300 11 (0200 11 (0100 13 (0000 14)))))))))
0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
1010 11 (0010 14 (0000 14))
0103 11 (0003 12 (0002 12 (0001 12 (0000 14))))
0210 11 (0110 13 (0010 14 (0000 14)))
0140 11 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))
0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))
0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))
0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))
0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))
0102 11 (0002 12 (0001 12 (0000 14)))
0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))
0112 11 (0012 12 (0011 12 (0010 14 (0000 14))))
0190 11 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0910 11 (0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14))))))))))
0810 11 (0710 11 (0610 11 (0510 11 (0410 11 (0310 11 (0210 11 (0110 13 (0010 14 (0000 14)))))))))
0101 11 (0100 13 (0000 14))
0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0120 11 (0110 13 (0010 14 (0000 14)))
0150 11 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0170 11 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0115 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0113 11 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
0022 10 (0012 12 (0011 12 (0010 14 (0000 14))))
2000 10 (1000 11 (0000 14))
3000 10 (2000 10 (1000 11 (0000 14)))
1100 10 (0100 13 (0000 14))
0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0106 10 (0105 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))
0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))
0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0072 10 (0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0033 10 (0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0109 10 (0009 11 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))))
0082 10 (0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))))
0052 10 (0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))
0117 10 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))
0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))
0031 10 (0030 12 (0020 12 (0010 14 (0000 14))))
0023 10 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))
2010 10 (1010 11 (0010 14 (0000 14)))
3010 10 (2010 10 (1010 11 (0010 14 (0000 14))))
0032 10 (0022 10 (0012 12 (0011 12 (0010 14 (0000 14)))))
1110 10 (0110 13 (0010 14 (0000 14)))
0118 10 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))))
0081 10 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))
0108 10 (0008 11 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14)))))))))
0051 10 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0116 10 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0062 10 (0061 10 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0071 10 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))))
0035 10 (0025 10 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14))))))))
0034 10 (0024 10 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))
0107 10 (0007 11 (0006 11 (0005 12 (0004 12 (0003 12 (0002 12 (0001 12 (0000 14))))))))
0042 10 (0041 10 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14))))))
0119 10 (0019 11 (0018 11 (0017 11 (0016 11 (0015 12 (0014 12 (0013 12 (0012 12 (0011 12 (0010 14 (0000 14)))))))))))
0021 10 (0011 12 (0010 14 (0000 14)))
0092 10 (0091 10 (0090 12 (0080 12 (0070 12 (0060 12 (0050 12 (0040 12 (0030 12 (0020 12 (0010 14 (0000 14)))))))))))

答案 3 :(得分:1)

只要最大总和很低,这个解决方案就可以正常工作。其复杂程度为O(N*M),其中N是列表数量,M是每个列表的大小。

创建一个二维数组[4,16],其中每个条目都包含此结构的列表:{ Reference, Index }

例如,在您的示例中,[1,9]处的条目会将Reference存储到单元格[0,6],而Index会存储对第二项中第一项的引用list(值3)。它们存储在单元格9中,因为总和为9,Index + Reference是获取值9的路径。每个单元格都存储此类参考组合的列表。

for (int i = 0; i < 4; i++)
{
  foreach (var v in lists[i])
  {
    if (i == 0)
    {
      result[i, v].Add({ Reference = null, Index = v.Index });
      continue;
    }
    for (int j = 15; j >= 0; j--)
    {
      result[i, v + j].Add({ Reference = result[i-1, j].Pointer, Index = v.Index });
    }
  }
}

现在你有15个树结构来遍历寻找最好的100个。你看results[3, 15]并递归遍历引用。如果您的最后一个(第四个)列表已排序,那么您可以在添加最后一个值时进行此遍历。其他列表不必排序。

答案 4 :(得分:0)

列表A:6,5,5,0,0,...... 列表B:3,1,1,0,0,......

一开始 数组: 6(来自listA的项目)并且未使用带有此项目索引的元素来自B(在开始时 - 0) 5和索引 ...

从此阵列中选择最大值。 增加所选项目的索引。

重复直到列表的最后一项设定索引 - (列表B中的元素数)。