所有子集中公共索引的值总和?

时间:2015-09-01 15:02:39

标签: c++ recursion

在最近的一个问题中,我必须在大小 n 的数组中,在所有可能的 k 子集中的公共索引处对所有值求和。

例如:如果

array ={1,2,3}

其子集(k = 2)将是(x [i],x [j]),其中i <1。 Ĵ

1 2
1 3
2 3
Sum:4,8

首先,我使用了递归(与生成所有子集相同)

int sum_index[k]={0};
void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
    if(q==k)
    {
       for(int i=0;i<k;i++)
          sum_index[i]+=temp[i];
    }
    else
    {
        for(int i=r;i<n;i++)
        {
            temp[q]=array[i];
            sub_set(value,n,k,temp,q+1,i+1);
        }
    }
}

问题是它花费了太多时间而不是预期。

然后我将其修改为......

void sub_set(int array[],int n,int k,int temp[],int q=0,int r=0)
{
    if(q==k)
    {
       return;
    }
    else
    {
        for(int i=r;i<n;i++)
        {
            temp[q]=array[i];
            sum_index[q]+=temp[q];  //or sum_index[q]+=s[i];
            sub_set(value,n,k,temp,q+1,i+1);
        }
    }
}

仍然花太多时间!!

这个问题还有其他方法吗?或者我需要的任何其他修改我不知道??

3 个答案:

答案 0 :(得分:1)

不要迭代可能的子集,而应将其视为组合学问题。

要使用k = 2和{1,2,3}的示例,让我们看一下结果的第一个值。它有两个1和一个2.两个1对应于可以从{2,3}制作的第一个元素集,而1对应于可以从{3}制作的一个元素集的数量。对于结果的第二个元素中的一个2和两个3,存在类似的排列,并查看在考虑元素之前出现的元素的子集。

当k> 2时,事情变得有点复杂,因为那时你将不得不在被考虑的元素之前和之后寻找元素组合的数量,但基本前提仍然有效。将之后的子集数量乘以可能的子集数量,然后将告诉您每个元素对结果的贡献次数。

答案 1 :(得分:1)

O(n ^ 2)中的解而不是O(n!):
首先是一个小小的(:))解释,然后是一些代码:

我将假设您的数组已排序(如果没有,请先使用std::sort)。另外,我将使用数组值1,2,3,4 ......这里,如果数组包含任意值(如2 8 17),你必须将它视为索引(即1 =&gt; 2,2 =&gt; 8等)

定义:(x choose y)表示binomial coefficient,它的计算方式也在链接中。如果数组大小为a且子集大小为k,则(a choose k)是排列的数量,例如。例如3:(1,2),(1,3)和(2,3)。

如果你在彼此之下写下排列,你想要每列的总和,如果你知道每个数组元素出现多少次,那就很容易了。第一列有多少1,2和3,第二列有多少(k = 2)。

这是一个更大的例子来解释:(1,2,3,4,5)和所有可能的k(每个在一个区块中):

1
2
3
4
5

12
13
14
15
23
24
25
34
35
45

123
124
125
134
135
145
234
235
245
345

... (didn´t write k=4)

12345

让我们介绍列索引0<=c<k,即。 c = 0表示第一列,c = 1表示第二列,依此类推;和数组大小s=5

所以,看,例如。在k=3 - 块,您会注意到以1开头的行(列c = 0)具有k=2的值(2,3,4,5)的所有排列,更一般地说列c中的值x具有值x+1s之后的所有排列。从x+1s的值是s-x个不同的值,在列c之后有k-c-1个列。因此,对于值x,您可以计算((s-x) choose (k-c-1)) 此外,第一列只有值1,2,3,最后两个数字不在这里,因为在此列之后有两个更多列。

如果您对第一列执行此操作,则效果很好。例如。在上面k = 3的第一列中值为1:
count(x) = ((s-x) choose (k-c-1)) = (4 choose 2) = 6
那里确实有六个1。计算每个数组值的计数,乘以x*count(x),并为每个x求和,这是第一列的结果。

其他列稍微难点,因为可能存在多个相同数字的“置换块”。首先,上面的步骤需要一个小的调整:你需要一个多重数组,每个数组值一个乘数,并在开始时每个乘数为1.在上面的计算x*count(x)中,取{{1}而是。

x*count(x)*muliplier(x) - 例子中,第一列中的1后面可以是2,3,4,后面可以是3,4,而后面是3,4,所以基于3的排列是第二列需要计数两次,而基于4列甚至三次;更普遍的是很多次,比如在previos colums中有较小的值。将其乘以当前乘数。

... 一些代码:

k=3

答案 2 :(得分:0)

std::next_permutation可能会有所帮助:

std::vector<int> sub_set(const std::vector<int>& a, int k)
{
    std::vector<int> res(k, 0);
    std::vector<bool> p(a.size() - k, false);
    p.resize(a.size(), true);

    do
    {
        int index = 0;
        for (std::size_t i = 0; i != p.size(); ++i) {
            if (p[i]) {
                res[index++] += a[i];
            }
        }
    } while (std::next_permutation(p.begin(), p.end()));
    return res;
}

Live Demo