2

So I just had a programming test for an interview and I consider myself a decent programmer, however I was unable to meet time constraints on the online test (and there was no debugger allowed). Essentially the question was give a range of indices [low, high] and a value to increase these indices by, after doing this M times to the array, find me the largest value.

So if you had an array of size 5 [0, 0, 0, 0, 0]
and you were given instructions
[0, 3] 143
[2, 4] 100
and [2,2] 100
the array would be [143, 143, 343, 243, 100]

and the highest would be 343.

I tried the naive solution but couldnt think of a slick algorithm and thought the answer had to be done by some memory copying?

How could one solve this issue the fastest? Is there something i am missing here?

Thanks

Community
  • 1
  • 1
user3086956
  • 55
  • 1
  • 7

5 Answers5

4

It isn't completely clear from you question whether the large array contains all zeros at the start, or whether you are given a large array with initial values, but similar methods can be used in both cases:

A) Large array of zeros

First of all, in this case there is no need to actually create the large array, or do anything with it.

Given these ranges and values:

[0, 3] 143
[2, 4] 100
[2, 2] 100

Create a list, where every low index is stored with the value, and every high index (plus 1) is stored with the inverse of the value:

{0, +143} {4, -143} {2, +100} {5, -100} {2, +100} {3, -100}

Then sort this list (and preferably merge values with the same index):

{0, +143} {2, +200} {3, -100} {4, -143} {5, -100}

Then, iterate over the list, keep a running total, and find the maximum value and its start and end index:

           total  
{0, +143}   143  
{2, +200}   343   <-- max  
{3, -100}   243   <-- end  
{4, -143}   100  
{5, -100}     0  

So the maximum value is 343, and its range is index 2 ~ 3 (so really only position 2).

The complexity of this algorithm is linear to the number of ranges M, but not influenced by the size of the large array N, so O(M).

B) Large array with initial values

If you are given an array with inital values, e.g.:

[300, 200, 400, 600, 700]

any element could still have the largest value after the values in the ranges have been increased, so in the end you have to iterate over every element in the array to find the maximum value.

However, you can avoid having to actually increase any values in the array, or iterate over the array more than once, by creating the same list as above:

{0, +143} {2, +200} {3, -100} {4, -143} {5, -100}

and then iterating over the array to find the maximum value, while keeping a running total of the additional values, and adding these to the values while comparing with the maximum value:

              total
0: {0, +143}   143   value: 300 + 143 = 443  
1: no change   143   value: 200 + 143 = 343  
2: {2, +200}   343   value: 400 + 343 = 743  
3: {3, -100}   243   value: 600 + 243 = 843   <-- max  
4: {4, -143}   100   value: 700 + 100 = 800   <-- end  
5: {5, -100}     0  

So the maximum value is 843, and its range is index 3 ~ 4 (so really only position 3).

The complexity of this algorithm is linear to the size of the large array N, and linear to the number of ranges M, or O(N+M), but assuming that N is much greater than M, this is ~ O(N).

  • 1
    I actually think I like your suggestion more than mine, I was keeping the ranges and values, but you only keep where the values change which I think is much simpler +1 – kmdreko Jun 13 '16 at 21:44
0

You can write your array conditions as rows of a matrix:

[0, 3] -> [1 1 1 0 0]

[2, 4] -> [0 0 1 1 1]

[2, 2] -> [0 0 1 0 0]

Then you write your values as a row vector [143 100 100] and multiply it for the above matrix (left multiplication). The maximum of the result vector is what you are looking for.

For general vector-matrix multiplication there are fast algorithms. Some example can be found there: http://web.stanford.edu/~rrwill/mat-vec-slides.pdf.

If you can assume that the above matrix is sparse (reasonable) then there are very efficient algorithms that perform the multiplication.

ilmarchese
  • 11
  • 8
  • 1
    Add one instruction [4,4,10000]. – Aleksei Guzev Jun 13 '16 at 21:01
  • using this method with a "large array" as OP's title specifies doesn't seem very efficient to me – kmdreko Jun 13 '16 at 21:16
  • @vu1p3n0x why your method is more efficient than mine ? You perform the same operations with the complication of rewriting the propagation of arrays (i cannot comment your answer because of low reputation) – ilmarchese Jun 13 '16 at 21:37
  • I don't advocate rewriting the arrays, I simply included that step for visualization purposes. I'm talking about only working with the ranges. See user3386109's answer as to why working with the ranges can be much more efficient. – kmdreko Jun 13 '16 at 21:40
0

The solution is to determine where the ranges overlap. The code needs to maintain a list of ranges, and as each input line is processed, the list must be updated. Assuming that there are only a small number of ranges compared with the number of items in the array, it will be much faster to process a table of ranges than naively update the array.

For example, lets's say that the array has ten million entries, and you were given two instructions

[0,5000000] 50
[4000000,6000000] 100

The naive solution will write 17 million entries in the array (10 million to initialize the array, and another 7 million to process the two instructions). But you can determine instantly that the max value is 150 because the two ranges overlap.

user3386109
  • 34,287
  • 7
  • 49
  • 68
0

The key part here is to work with the ranges and not with the array (until the end). What you can do is merge and split the ranges to keep a list of modifications. (the intermediate array is only used to visualize how the ranges overlap, you don't need to modify an array for each range added)

Start with an empty range (all zeros)

{0, 5, 0} -> [0, 0, 0, 0, 0]

add the first range

{0, 4, 0  } -> [0,   0,   0,   0,   0]
{0, 3, 143} -> [143, 143, 143]
               [143, 143, 143, 0,   0] -> {0, 2, 143}, {3, 4, 0}

so you now have 2 ranges

{0, 3, 143}, {4, 5, 0} -> [143, 143, 143, 0,   0  ]
{2, 4, 100}            ->           [100, 100, 100]
                          [143, 143, 243, 100, 100] -> {0, 1, 143}, {2, 2, 243}, {4, 5, 100}

and now 3 and so on... When you get to the end you can just search the list of ranges for the largest.

The tricky part here of course is knowing how to merge, add, and split the ranges. If the beginning of an added range is within the range of another, then the first one must be split in two. Same as when the added range ends within the range of another. If the added range overlaps the whole range of another, then the range's value is simply added to it.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
0

Detect local maximums while running through the instructions. The answer will be the maximum of local maximums. Please, note that the vector<Instruction> p must be sorted by start.

struct Instruction
{
    size_t start, stop;
    value_t value;

    bool operator > (const Instruction& b) const
    {
        return stop > b.stop;
    }
};

template<class Ti>
value_t do_work(const Ti&& b, const Ti&& e)
{
    value_t result = 0;
    value_t local = 0;

    auto q = priority_queue<Instruction, 
                 vector<Instruction>, 
                 greater<Instruction>>();

    for (auto i=b; i!=e; ++i)
    {
        q.push(*i);
        if (q.top().stop < i->start)
        {
            if (local > result)result = local;
            do
            {
                local -= q.top().value;
                q.pop();
            } while (q.top().stop < i->start);
        }

        local += i->value;
    }

    return max(local, result);
}

int main()
{
    vector<Instruction> p = { 
        {0,3,143}, {2,4,100}, {2,2,100}, 
        {3,5,1000}, {4,4,500} 
    };

    cout << do_work(begin(p),end(p)) << endl;

    return 0;
}
Aleksei Guzev
  • 301
  • 2
  • 14
  • 2
    Think real hard on how much fun it is to work with someone who graduated because other people did their homework. – user4581301 Jun 13 '16 at 23:02