Reputation:
I'm trying to optimize a slow part of a larger algorithm.
I have an array of random-looking numbers:
[295, 292, 208, 393, 394, 291, 182, 145, 175, 288, 71, 86, 396, 422]
(actual arrays are much longer), and an index: N
(N = 5
)
What I want to do is subtract subtract 1 from the last M elements for each of the first N elements that are smaller
So (pseudo-code):
for a = 1..5
for b = 6..N
if ary[a] < ary[b]
ary[b]--;
Obviously this is a horribly inefficient O(N^2) algorithm. I'm trying to think of a faster way to do it, but can't. It seems like I should be able to pre-compute the values to subtract somehow, and reduce it to:
for a = 1..5
// ???
for b = 6..N
ary[b] -= ???
but I'm missing something.
[edit] I feel like an idiot. I didn't properly explain what I want, fixed.
Upvotes: 3
Views: 95
Reputation: 853
It seems to me that Petr's solution doesn't follow the specification of the problem: we should subtract 1 (one) for each "hit", not the VALUE of ary[a].
Here's an alternative:
place the testvalues ary[j], j=0..N-1 in a SORTED array: sortedSmallAry[N].
Then run over all b, i.e. b=N, ..., Total-1, and for each ary[b]:
run j=0..N-1 over the sortedSmallAry, test whether sortedSmallAry[j] is smaller than ary[b], count the hits, exit as soon as it fails (because it's sorted). If N is relatively large you can use a binary search in sortedSmallAray to determine how many of its elements satisfy the condition.
Subtract the hitcount from ary[b].
Upvotes: 0
Reputation: 9997
Let's reorganize the loops:
for b = 6..N
for a = 1..5
if ary[a] < ary[b]
ary[b] -= ary[a];
The result will be the same. But now the logic is more clear: from each element from the second part of array you subtract ary[1]
, then ary[2]
and so on until some ary[a]
is bigger that what remains from ary[b]
.
This can be optimized the following way. Calculate the cumulative sums of the first half of array: let sum[1]=ary[1]
, sum[2]=sum[1]+ary[2]
, sum[3]=sum[2]+ary[3]
, and so on. This can be done in O(N)
.
Now for each b
you need to find such a_last
that sum[a_last]<ary[b]
, but sum[a_last+1]>=ary[b]
-- this will mean that from ary[b]
you will subtract ary[1]+...+ary[a_last]=sum[a_last]
. You can find such a_last
using binary search in O(log N
), thus making the overall algorithm O(N log N)
.
The pseudocode:
sum[0] = 0
for a = 1..5
sum[a] = sum[a-1] + ary[a]
for b = 6..N
a_last = maximal a such that sum[a]<ary[b] // use binary search
ary[b] -= sum[a_last]
Upvotes: 2