C++ 具有固定子集大小的完全和问题

C++ 具有固定子集大小的完全和问题,c++,algorithm,time-complexity,subset,C++,Algorithm,Time Complexity,Subset,我正在寻找一种时间复杂度最低的算法,该算法可以解决完美和问题的一种变体(最初:从大小为n的整数数组[*]中找到所有可变大小的子集组合,这些整数和特定的数x)其中,子集组合大小为固定大小k,并返回可能的组合,而不包含直接和间接的重复项(当有一个组合包含另一个顺序中完全相同的元素时) 我知道这个问题是NP难的,所以我不希望有一个完美的通用解决方案,但在我的情况下,至少可以在合理的时间内运行,n接近1000,k大约10 到目前为止我已经尝试过的事情: 找到一个组合,然后对其进行连续修改及其修改 假设

我正在寻找一种时间复杂度最低的算法,该算法可以解决完美和问题的一种变体(最初:从大小为
n
的整数数组[*]中找到所有可变大小的子集组合,这些整数和特定的数
x
)其中,子集组合大小为固定大小
k
,并返回可能的组合,而不包含直接和间接的重复项(当有一个组合包含另一个顺序中完全相同的元素时)

我知道这个问题是NP难的,所以我不希望有一个完美的通用解决方案,但在我的情况下,至少可以在合理的时间内运行,
n
接近1000,
k
大约10

到目前为止我已经尝试过的事情:

  • 找到一个组合,然后对其进行连续修改及其修改

    假设我有一个数组,例如:

所以我有
n=8
,我想
x=10
k=3

多亏了一些晦涩难懂的方法(bruteforce?),我发现了一个子集
[3,3,4]

从这个子集中,我找到了其他可能的组合,从中取出两个元素,并将它们替换为求和相同的其他元素,即
(3,3)
可以替换为
(1,5)
,因为这两个元素都得到了相同的和,并且替换的数字还没有被使用。因此,我获得另一个子集
[1,5,4]
,然后对所有获得的子集重复这个过程。。。无限期

这里提出的主要问题是,很难确定何时完成,而且这种方法相当混乱。我想象过这种方法的一些变体,但它们确实在进行中

  • 在集合中迭代以列出总和为x的所有
    k
    长组合
非常不言自明。这是一种幼稚的方法,在我的例子中效果不好,因为我有一个相当大的
n
和一个
k
,它不足以避免灾难性的大量组合(组合数量的大小是10^27!)

我尝试了几种与设置研究领域相关的机制,而不是愚蠢地重复所有可能性,但这相当复杂,仍在进行中

你有什么建议?(代码段可以是任何语言,但我更喜欢C++)


[*]为了澄清关于基集合是否可以包含重复项的疑问,为了更精确,我使用了术语“数组”而不是“集合”。在我的例子中,集合可以包含重复的整数,并且相当多,对于1000个元素,有70个不同的整数(四舍五入计数),例如,您应该首先对所谓的数组进行排序。其次,你应该确定问题是否真的可以解决,以节省时间。。。所以你要做的是取最后的k个元素,看看它们的和是否大于或等于x值,如果它小于,你就完成了,不可能做这样的事情。。。。如果它实际上是相等的,是的,你也做了,没有其他排列。。。。O(n)感觉很好,不是吗??如果它比你要大,你有很多工作要做。。。。。您需要将所有排列存储在一个单独的数组中。。。。然后继续,用数组中最小的元素替换k个数中最小的。。。。如果这个仍然大于x,那么在第二个和第三个,依此类推,直到得到小于x的值。一旦你到达一个点,你的总和小于x,你可以继续,并开始增加你停在的最后一个位置的值,直到你达到x。。。。一旦你击中x,这就是你的组合。。。。然后你可以继续得到上一个元素,如果你的东西中有1,1,5,6,你也可以继续抓住1,把它加到最小的元素中,5得到6,接下来你检查,你能把这个数字6写成两个值的组合吗,一旦你碰到这个值,你就停下来。。。。然后你也可以为其他人重复。。。。在最坏的情况下,你的问题可以在O(n!)时间内解决。。。。我不建议你使用10^27组合,这意味着你有超过10^27个元素,嗯,这是个坏主意,你有那么多空间吗???这就好比3位表示头,8位表示每个整数你需要9.8765*10^25 TB来存储clossal数组,比超级计算机还多的内存,你应该担心你的计算机是否能存储这个怪物,而不是你是否能解决这个问题,那么多的组合,即使你找到了一个二次解,也会使你的计算机崩溃,你知道什么是二次解,离O(n!)还有很长的路要走。

你应该首先对所谓的数组进行排序。其次,你应该确定问题是否真的可以解决,以节省时间。。。所以你要做的是取最后的k个元素,看看它们的和是否大于或等于x值,如果它小于,你就完成了,不可能做这样的事情。。。。如果它实际上是相等的,是的,你也做了,没有其他排列。。。。O(n)感觉很好,不是吗??如果它比你要大,你有很多工作要做。。。。。您需要将所有排列存储在一个单独的数组中。。。。然后继续,用数组中最小的元素替换k个数中最小的。。。。如果这个仍然大于x,那么在第二个和第三个,依此类推,直到得到小于x的值。一旦你到达一个点,你的总和小于x,你可以继续,并开始增加你停在的最后一个位置的值,直到你达到x。。。。一旦你击中x,这就是你的组合。。。。然后你可以继续得到上一个元素,如果你的东西中有1,1,5,6,你也可以继续抓住1,把它加到最小的元素中,5得到6,接下来你检查,你能把这个数字6写成两个值的组合吗,一旦你碰到这个值,你就停下来。。。。那你就可以
s = [1,2,3,3,4,5,6,9]
setSumStructure find(int[] set, int x, int k, int setIdx)
{
   int sz = set.length - setIdx;
   if (sz < x) return null;
   if (sz == x) check sum of set[setIdx] -> set[set.size] == k.  if it does, return the set together with the sum, else return null;
   
   for (int i = setIdx; i < set.size - (k - 1); i++)
      filter(find (set, x - set[i], k - 1, i + 1));

   return filteredSets;
}
Lst := [1,2,3,3,4,5,6,7];
k := 3;
sum := 10;

  3  3  4
  2  3  5  //distinct 3's
  2  3  5
  1  4  5
  1  3  6   
  1  3  6   //distinct 3's
  1  2  7
int main()
{
    vector<vector<vector<int>>> A;
    vector<int> Lst = { 1, 2, 3, 3, 4, 5, 6, 7 };

    int k = 3;
    int sum = 10;
    A.push_back({ {0} });  //fictive array to make non-empty variant
    for (int i = 0; i < sum; i++)
        A.push_back({{}});


    for (int item : Lst) {
        for (int i = sum; i >= item; i--) {
            for (int j = 0; j < A[i - item].size(); j++) 
                if (A[i - item][j].size() < k + 1  && 
                    A[i - item][j].size() > 0) {
                    vector<int> t = A[i - item][j];
                    t.push_back(item);
                    A[i].push_back(t);  //add new variant including current item
                }
        }
    }
         //output needed variants
    for (int i = 0; i < A[sum].size(); i++)
        if (A[sum][i].size() == k + 1) {
            for (int j  = 1; j < A[sum][i].size(); j++) //excluding fictive 0
                cout << A[sum][i][j] << " ";
        cout << endl;
    }
}
from collections import namedtuple
# This is a doubly linked list.
# (value, tail) will be one group of solutions.  (next_answer) is another.
SumPath = namedtuple('SumPath', 'value tail next_answer')

def fixed_sum_paths (array, target, count):
    # First find counts of values to handle duplications.
    value_repeats = {}
    for value in array:
        if value in value_repeats:
            value_repeats[value] += 1
        else:
            value_repeats[value] = 1

    # paths[depth][x] will be all subsets of size depth that sum to x.
    paths = [{} for i in range(count+1)]

    # First we add the empty set.
    paths[0][0] = SumPath(value=None, tail=None, next_answer=None)

    # Now we start adding values to it.
    for value, repeats in value_repeats.items():
        # Reversed depth avoids seeing paths we will find using this value.
        for depth in reversed(range(len(paths))):
            for result, path in paths[depth].items():
                for i in range(1, repeats+1):
                    if count < i + depth:
                        # Do not fill in too deep.
                        break
                    result += value
                    if result in paths[depth+i]:
                        path = SumPath(
                            value=value,
                            tail=path,
                            next_answer=paths[depth+i][result]
                            )
                    else:
                        path = SumPath(
                            value=value,
                            tail=path,
                            next_answer=None
                            )
                    paths[depth+i][result] = path

                    # Subtle bug fix, a path for value, value
                    # should not lead to value, other_value because
                    # we already inserted that first.
                    path = SumPath(
                        value=value,
                        tail=path.tail,
                        next_answer=None
                        )
    return paths[count][target]

def path_iter(paths):
    if paths.value is None:
        # We are the tail
        yield []
    else:
        while paths is not None:
            value = paths.value
            for answer in path_iter(paths.tail):
                answer.append(value)
                yield answer
            paths = paths.next_answer

def fixed_sums (array, target, count):
    paths = fixed_sum_paths(array, target, count)
    return path_iter(paths)

for path in fixed_sums([1,2,3,3,4,5,6,9], 10, 3):
    print(path)
[1, 3, 6]
[1, 4, 5]
[2, 3, 5]
[3, 3, 4]