所有子集中公共索引处的值之和
Sum of values at common indexes in all subsets?
在最近的一个问题中,我必须对大小为n的数组中大小为k的所有可能子集的公共索引的所有值求和。
For example: If
array ={1,2,3}
其子集(k=2)为(x [i], x [j]),其中i
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);
}
}
}
还是花了太多时间!!
这个问题还有别的办法吗?或者其他我不知道的修改??
与其在可能的子集中迭代,不如把它看作是一个组合问题。
要使用k=2和{1,2,3}的例子,让我们看看结果的第一个值。它有两个1和一个2。两个1对应于可以由{2,3}组成的第一个元素集,一个2对应于可以由{3}组成的第一个元素集的数量。对于结果的第二个元素中的一个2和两个3,也存在类似的安排,并查看在所考虑的元素之前出现的元素的子集。
当k>2时,事情变得有点复杂,因为这时你必须在考虑的元素之前和之后寻找元素组合的数量,但基本前提仍然有效。将之前的可能子集的数量乘以之后的子集的数量,这将告诉您每个元素对结果的贡献次数。
用O(n^2)代替O(n!)的解:
首先是一个小小的(:))解释,然后是一些代码:
我将在这里假设您的数组已排序(如果没有,则先使用std::sort
)。此外,我将使用数组值1,2,3,4…在这里,如果数组包含任意值(如2 8 17
),则必须将其视为索引(即。1=>2, 2=>8等)
定义:(x choose y)
表示二项式系数,其计算方法也在链接中。如果数组大小为a,子集大小为k,则(a choose k)
是排列的数量,例如。例如:(1,2),(1,3)和(2,3)。
如果您将排列顺序写在彼此下面,则需要每列的和,如果您知道每个数组元素在每列中出现的次数,则这将很容易。第一列有多少1 ',2 '和3 ',第二列有多少(k=2)。
这里有一个更大的例子来解释:(1,2,3,4,5)和所有可能的k´s(每个在一个块中):
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+1
到s
的所有值排列。从x+1
到s
的值是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
确实有6个1。为每个数组值计算这个计数,乘以x*count(x)
,并对每个x求和,这就是第一列的结果。
其他列稍微难一点,因为可能有多个相同数量的"排列块"。首先,上面的步骤需要做一个小的调整:您需要一个乘数数组,每个数组值对应一个乘数,开始时每个乘数为1。在上面的计算x*count(x)
中,取x*count(x)*muliplier(x)
代替。
在k=3
-example中,第一列的1可以跟2、3、4,2可以跟3、4,3可以跟4。所以第二列的基于3的排列需要计算两次,基于4的排列需要计算三次;更一般地说,前面的列中有更小的值。与当前乘数相乘。
…一些代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// factorial (x!)
unsigned long long fact(unsigned char x)
{
unsigned long long res = 1;
while(x)
{
res *= x;
x--;
}
return res;
}
//binomial coefficient (n choose k)
unsigned long long binom(unsigned char n, unsigned char k)
{
if(!n || !k) return 1;
return (fact(n) / fact(k)) / fact(n-k);
}
//just for convenience
template<class T> void printvector(std::vector<T> data)
{
for(auto l : data) cout << l << " ";
cout << endl;
}
std::vector<unsigned long long> calculate(std::vector<int> data, int k)
{
std::vector<unsigned long long> res(k, 0); //result data
std::vector<unsigned long long> multiplier(data.size(), 1);
if(k < 1 || k > 255 || data.size() < 1) return res; //invalid stuff
std::sort(data.begin(), data.end()); //as described
for(int column = 0; column < k; column++) //each column separately
{
//count what to multiply to the multiplier array later
std::vector<unsigned long long> newmultiplier(data.size(), 0);
//for each array element in this column
for(int x = column; x <= (data.size() + column - k); x++)
{
//core calculation
res[column] += data[x] * multiplier[x] * binom(data.size() - x - 1, k - column - 1);
//counting the new multiplier factor
for(int helper = x + 1; helper < data.size(); helper++)
newmultiplier[helper]++;
}
//calculating new multiplier
for(int x = 0; x < data.size(); x++)
{
if(newmultiplier[x])
multiplier[x] *= newmultiplier[x];
}
}
return res;
}
int main() {
printvector(calculate({1,2,3}, 2)); //output 4 8
return 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;
}
现场演示
- 数组索引的值没有增加
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- 查找最接近的大于当前数字的数字的索引
- 在C++中调整向量中的索引
- 重载元组索引运算符-C++
- 如何从存储在std::映射中的std::集中删除元素
- 给定一个向量,如何找到该向量的所有子集和的原始索引
- 为std::string的某个索引赋值
- 并行用于C++17中数组索引范围内的循环
- 跟随整数索引列表的自定义类迭代器
- 如何在for循环中包含两个索引值的测试条件
- D3D11-将混合权重和索引传递到顶点着色器
- 将转换字符键入 int 以用作向量C++的索引
- 在 C++ 中访问数组负索引处的内存不会返回垃圾
- 将非平凡的项目放在多集中
- 如何为圆环创建索引
- 在子集化后将包含索引号的列表列表映射到标准索引序列
- 查找字符在两个索引之间出现的次数
- Azure Kinect 使用正文索引映射裁剪正文
- 如何查找哪个类对象位于数组的特定索引上(多态性)