0

Let's say I have an array A = {a1, a2, ..., an}

Suppose A is divided in blocks of size b

What I'm trying to do is:

Let MinUntilHere and MinStartingHere be 2 arrays of size n, where:

  • MinUntilHere[i] = minimum from the beginning of i's block until i
  • MinStartingHere[i] = minimum from i until the end of i's block

I want to fill this 2 arrays in a single transversal in the array... I was trying the following pseudocode:

int fill(int pos = 0){

    if(pos >= n) return INF

    if(pos is the start index of a block){ //i % b == 0
        MinUntilHere[pos] = A[pos]
        MinStartingHere[pos] = min(A[pos], fill(i + 1))
    }
    else{
        MinUntilHere[pos] = min(A[pos], A[pos - 1])
        MinStartingHere[pos] = min(A[pos], fill(i + 1))
        return MinStartingHere[pos]
    }
}

but as you can see, it's not complete, since it can't detect when each block is over and some returns are missing. How can I make a function that go through the array only once and compute my answers?

Example:

Let A = {2,1,3,6,5,4} and b = 2, the arrays must be filled like this:

MinUntilHere =    {2,1,3,3,5,4}

MinStartingHere = {1,1,3,6,4,4}

MinUntilHere[0] = 2 cause from the beginning of 0's block until index 0, the minimum is 2.

MinUntilHere[1] = 1 cause from the beginning of 1's block until index 1, the minimum is min(1,2) = 1

End of Example
2
  • 1
    Shouldn't MinStartingHere be {1,1,3,6,4,4}? Commented Aug 5, 2016 at 16:01
  • @AbhishekBansal sure, thanks for helping, fixed :) Commented Aug 5, 2016 at 16:04

3 Answers 3

1

You may use the following which does the job in 2 passes:

template <typename InputIt, typename OutputIt>
void ComputeMinUntilHere(InputIt begin, InputIt end, OutputIt output)
{
    if (begin == end) {
        return;
    }
    auto min = *begin;
    while (begin != end) {
        min = std::min(min, *begin);
        *output = min;
        ++begin;
        ++output;
    }
}

struct MinByBlock
{
    std::vector<int> minUntilHere;
    std::vector<int> minStartingHere;
};

MinByBlock ComputeMinByBlock(const std::vector<int>&v, std::size_t blockSize)
{
    MinByBlock res;
    res.minUntilHere.resize(v.size());
    res.minStartingHere.resize(v.size());
    for (std::size_t i = 0; i < v.size(); i += blockSize) {
        const auto blockBegin = v.begin() + i;
        const auto blockEndIndex = std::min(i + blockSize, v.size());
        const auto blockEnd = v.begin() + blockEndIndex;

        ComputeMinUntilHere(blockBegin, blockEnd, res.minUntilHere.begin() + i);
        ComputeMinUntilHere(std::make_reverse_iterator(blockEnd),
                            std::make_reverse_iterator(blockBegin),
                            std::make_reverse_iterator(res.minStartingHere.begin() + blockEndIndex));
    }
    return res;
}

Demo

You may notice than minUntilHere and minUntilHere are similar and just depends if you iterate from left to right or right to left.

ComputeMinUntilHere do the job for an entire range.

ComputeMinByBlock split the vector by independent blocks to fill result by block.

Sign up to request clarification or add additional context in comments.

Comments

0

In the general case you cannot hope to fill the arrays in a single pass.

For example, if the block size is N (entire array), then to fill the entry MinUntilHere[i], you need to have looked at all items before i and to fill the entry MinStartingHere[i] you need to have looked at all items after i.

2 Comments

this is the reason im using recursion. I can have a list L and do something like Min(pos) = if( pos >= |L| ) return INF; else return min(L[pos], Min(pos+1);
One pass by array is sufficient BTW, so a total of 2 passes.
0

Sorry guys, but I was able to find a linear solution (running the array just once) with code much smaller. This is the solution:

int fill(int pos = 0){

    if(pos >= |A|) return INF;

    int mod = pos % b;

    if(mod == 0){
        MinUntilHere[pos] = A[pos];
        MinStartingHere[pos] = min(A[pos], fill(pos + 1));
        return fill(pos + bsize);
    }
    else if(mod == bsize - 1){
        MinUntilHere[pos] = min(MinUntilHere[pos-1], A[pos]);
        return MinStartingHere[pos] = A[pos];
    }
    else{
        MinUntilHere[pos] = min(MinUntilHere[pos-1], A[pos]);
        return MinStartingHere[pos] = min(A[pos], fill(pos + 1));
    }

}

It works like this:

  • If I'm starting the block, set MinUntilHere as the A[pos] element, and make the MinStartingHere[pos] as the minimum of A[pos] and the return of fill(pos + a);
  • If It's not start nor end of a block, just make MinUntilHere[pos] = min(A[pos], MinUntilHere[pos-1]) and for the MinStartingHere, set him as the minimum between A[pos] and fill(pos+1) and return him (to the previous recursion)
  • If it's a block end, do the same for MinUntilHere, and for MinStartingHere, just set it as A[pos] and return it to the previous recursions.

An important detail is that when you start the block, you call a lot of recursions and then come back to the beginning of that block with the value for MinStartingHere, and AFTER that you call the method for pos + b, that means it will start to compute a new block after finishing the current one.

Thanks for the help anyway :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.