我在C ++中做了一个测试,要求一个函数返回一个索引,它将输入向量分成两部分,具有相同的元素总和,例如:对于vec = {1, 2, 3, 5, 4, -1, 1, 1, 2, -1}
,它可能返回3,因为1 + 2 + 3 = 6 = 4-1 + 1 + 1 + 2-1。所以我完成了返回正确答案的函数:
int func(const std::vector< int >& vecIn)
{
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (std::accumulator(vecIn.begin(), vecIn.begin() + p, 0) ==
std::accumulator(vecIn.begin() + p + 1, vecIn.end(), 0))
return p;
}
return -1;
}
我的问题是当输入是一个只包含1(或-1)的非常长的向量时,函数的返回很慢。所以我想到从中间开始搜索想要的索引,然后左右移动。但我认为最好的方法是索引采用合并排序算法顺序,即:n / 2,n / 4,3n / 4,n / 8,3n / 8,5n / 8,7n / 8 ...其中n是向量的大小。有没有办法在公式中编写这个顺序,所以我可以在我的函数中应用它?
由于
修改 经过一些评论之后我不得不提到我几天前已经完成了测试,所以我忘了提及没有解决方案的部分:它应该返回-1 ...我已经更新了问题标题。
答案 0 :(得分:10)
特别针对这个问题,我会使用以下算法:
这给出了o(n)算法而不是o(n2)
答案 1 :(得分:4)
您可以更快地解决问题而无需在每一步调用std::accumulator
:
int func(const std::vector< int >& vecIn)
{
int s1 = 0;
int s2 = std::accumulator(vecIn.begin(), vecIn.end(), 0);
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (s1 == s2)
return p;
s1 += vecIn[p];
s2 -= vecIn[p];
}
}
这是O(n)
。在每一步中,s1
将包含第一个p
元素的总和,以及s2
其余元素的总和。移动到下一个元素时,可以使用加法和减法更新它们。
由于std::accumulator
需要迭代您提供的范围,因此您的算法为O(n^2)
,这就是为什么它对于许多元素来说太慢了。
答案 2 :(得分:2)
考虑到MSalters的评论,我担心另一种解决方案会更好。如果您想使用更少的内存,可能选择的答案已经足够好了,但要找到可能的多个解决方案,您可以使用以下代码:
static const int arr[] = {5,-10,10,-10,10,1,1,1,1,1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
// compute cumulative sum
std::vector<int> cumulative_sum( vec.size() );
cumulative_sum[0] = vec[0];
for ( size_t i = 1; i < vec.size(); i++ )
{ cumulative_sum[i] = cumulative_sum[i-1] + vec[i]; }
const int complete_sum = cumulative_sum.back();
// find multiple solutions, if there are any
const int complete_sum_half = complete_sum / 2; // suggesting this is valid...
std::vector<int>::iterator it = cumulative_sum.begin();
std::vector<int> mid_indices;
do {
it = std::find( it, cumulative_sum.end(), complete_sum_half );
if ( it != cumulative_sum.end() )
{ mid_indices.push_back( it - cumulative_sum.begin() ); ++it; }
} while( it != cumulative_sum.end() );
for ( size_t i = 0; i < mid_indices.size(); i++ )
{ std::cout << mid_indices[i] << std::endl; }
std::cout << "Split behind these indices to obtain two equal halfs." << std::endl;
这样,您就可以获得所有可能的解决方案。如果没有解决方案将矢量分成两个相等的半部分,则mid_indices将保留为空。 同样,您只需要将每个值相加一次。
我的建议如下:
static const int arr[] = {1,2,3,5,4,-1,1,1,2,-1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
int idx1(0), idx2(vec.size()-1);
int sum1(0), sum2(0);
int idxMid = -1;
do {
// fast access without using the index each time.
const int& val1 = vec[idx1];
const int& val2 = vec[idx2];
// Precompute the next (possible) sum values.
const int nSum1 = sum1 + val1;
const int nSum2 = sum2 + val2;
// move the index considering the balanace between the
// left and right sum.
if ( sum1 - nSum2 < sum2 - nSum1 )
{ sum1 = nSum1; idx1++; }
else
{ sum2 = nSum2; idx2--; }
if ( idx1 >= idx2 ){ idxMid = idx2; }
} while( idxMid < 0 && idx2 >= 0 && idx1 < vec.size() );
std::cout << idxMid << std::endl;
无论有多少值,它都会只添加一次值。这样它的复杂性仅为O(n)而不是O(n ^ 2)。
代码只是从左右同时运行,如果它的一侧比另一侧低,则进一步移动索引。
答案 3 :(得分:2)
回答实际问题:您的序列n / 2,n / 4,3n / 5,n / 8,3n / 8可以改写为
1*n/2
1*n/4 3*n/4
1*n/8 3*n/8 5*n/8 7*n/8
...
也就是说,分母从i = 2开始以2的幂运行,并且分子以2的步长从j = 1运行到i-1。但是,这不是您实际问题所需要的,因为你给出的例子有n = 10。显然你不想要n / 4 - 你的指数必须是整数。
这里最好的解决办法是递归。给定范围[b,e],选择中间值(b + e / 2)并将新范围设置为[b,(b + e / 2)-1]和[(b + e / 2)= 1 ,e]。当然,专长范围长度为1或2。
答案 4 :(得分:1)
你想要你提到的系列的n th 术语。那就是:
numerator: (n - 2^((int)(log2 n)) ) *2 + 1
denominator: 2^((int)(log2 n) + 1)
答案 5 :(得分:1)
我在Codility测试中遇到了同样的问题。上面有一个类似的答案(没有通过一些单元测试),但在代码段下面的测试成功。
#include <vector>
#include <numeric>
#include <iostream>
using namespace std;
// Returns -1 if equilibrium point is not found
// use long long to support bigger ranges
int FindEquilibriumPoint(vector<long> &values) {
long long lower = 0;
long long upper = std::accumulate(values.begin(), values.end(), 0);
for (std::size_t i = 0; i < values.size(); i++) {
upper -= values[i];
if (lower == upper) {
return i;
}
lower += values[i];
}
return -1;
}
int main() {
vector<long> v = {-1, 3, -4, 5, 1, -6, 2, 1};
cout << "Equilibrium Point:" << FindEquilibriumPoint(v) << endl;
return 0;
}
输出 平衡点:1
答案 6 :(得分:0)
这是Javascript中的算法:
function equi(arr){
var N = arr.length;
if (N == 0){ return -1};
var suma = 0;
for (var i=0; i<N; i++){
suma += arr[i];
}
var suma_iz = 0;
for(i=0; i<N; i++){
var suma_de = suma - suma_iz - arr[i];
if (suma_iz == suma_de){
return i};
suma_iz += arr[i];
}
return -1;
}
如您所见,此代码满足O(n)
的条件