The Bear's Den

Enter at your own risk

Different Peaks

Task 1: Max Diff

Submitted by: Mohammad Sajid Anwar


You are given an array of integers having four or more elements.

Write a script to find two pairs of numbers from this list (four numbers total) so that the difference between their products is as large as possible.

In the end return the max difference.

With Two pairs (a, b) and (c, d), the product difference is (a * b) - (c * d).

Example 1

Input: @ints = (5, 9, 3, 4, 6)
Output: 42

Pair 1: (9, 6)
Pair 2: (3, 4)
Product Diff: (9 * 6) - (3 * 4) => 54 - 12 => 42

Example 2

Input: @ints = (1, -2, 3, -4)
Output: 10

Pair 1: (1, -2)
Pair 2: (3, -4)

Example 3

Input: @ints = (-3, -1, -2, -4)
Output: 10

Pair 1: (-1, -2)
Pair 2: (-3, -4)

Example 4

Input: @ints = (10, 2, 0, 5, 1)
Output: 50

Pair 1: (10, 5)
Pair 2: (0, 1)

Example 5

Input: @ints = (7, 8, 9, 10, 10)
Output: 44

Pair 1: (10, 10)
Pair 2: (7, 8)

Solution

First insight on this task: The solution for a specific list depends not only on the structure of the given data - especially the number of positive and negative elements - but also on the values themselves.

Secondly: For the maximum product difference \(ab - cd\) there are two cases:

Here are two examples to illustrate these insights:

  1. The numbers are \(i = (4, 3, 2, 1, -6, -7)\).
    The global maximum product is \((-6)\cdot(-7) = 42\), the minimum product from the remaining list is \(2 \cdot 1 = 2\) with a difference of \(42 - 2 = 40\).
    The global minimum product is \(4 \cdot (-7) = -28\), the maximum product from the remaining list is \(3 \cdot 2 = 6\) with a difference of \(6 - (-28) = 34\)
    The maximum difference of \(40\) is taken with the maximum product.
  2. The numbers are \(i = (5, 3, 2, 1, -6, -7)\).
    The global maximum product is \((-6)\cdot(-7) = 42\), the minimum product from the remaining list is \(2 \cdot 1 = 2\) with a difference of \(42 - 2 = 40\).
    The global minimum product is \(5 \cdot (-7) = -35\), the maximum product from the remaining list is \(3 \cdot 2 = 6\) with a difference of \(6 - (-35) = 41\)
    The maximum difference of \(41\) is taken with the minimum product.

The maximum product can be found as:

The minimum product can be found as:

After identifying a global minimum or maximum product, the corresponding elements have to be invalidated for the following operation on the remaining list.

Though this approach may look a bit complicated, it has a complexity of \(\mathcal{O}(n)\), which may be regarded as optimal.

Putting everything together results in a somewhat lengthy solution:

use strict;
use warnings;
use experimental 'signatures';
use PDL;
use PDL::NiceSlice;

sub max_diff (@n) {
    my $n = long @n;
    $n->badflag(1);
    my $pd = null;
    for my $subs ([\&find_max, \&find_min, 1], [\&find_min, \&find_max, -1]) {
        my $nc = $n->copy;
        my $m1 = &{$subs->[0]}($nc);
        my $m2 = &{$subs->[1]}($nc->where($nc->isgood));
        $pd = $pd->append($subs->[2] * ($m1 - $m2));
    }

    max $pd;
}

sub remove_prod ($l) {
    my $prod = $l->prod;
    $l .= $l->badvalue;

    $prod;
}

sub find_max ($n) {
    goto &remove_prod if $n->nelem == 2;

    my ($pos, $neg) = which_both $n >= 0;
    my $max_ind = null;
    if ($pos->nelem >= 2) {
        $max_ind = $max_ind->glue(1, maximum_n_ind($n, 2));
    }
    if ($neg->nelem >= 2) {
        $max_ind = $max_ind->glue(1, minimum_n_ind($n, 2));
    }
    my $max = $n->index1d($max_ind)->prodover->maximum_ind;

    return remove_prod($n($max_ind(,($max))));
}

sub find_min ($n) {
    goto &remove_prod if $n->nelem == 2;

    my ($pos, $neg) = which_both $n >= 0;
    if (!$neg->isempty && !$pos->isempty) {
        return remove_prod($n(cat(($n->minmaximum)[2, 3])));
    } elsif ($pos->isempty) {
        return remove_prod($n(maximum_n_ind($n, 2)));
    } else {
        return remove_prod($n(minimum_n_ind($n, 2)));
    }
}

See the full solution to task 1.

Task 2: Peak Point

Submitted by: Mohammad Sajid Anwar


You are given an array of altitude gain.

Write a script to find the peak point gained.

Example 1

Input: @gain = (-5, 1, 5, -9, 2)
Output: 1

start: 0
1st change:  0 + (-5) = -5
2nd change: -5 + 1    = -4
3rd change: -4 + 5    = 1
4th change:  1 + (-9) = -8
5th change: -8 + 2    = -6

max(0, -5, -4, 1, -8, -6) = 1

Example 2

Input: @gain = (10, 10, 10, -25)
Output: 30

start: 0
1st change:  0 + 10    = 10
2nd change: 10 + 10    = 20
3rd change: 20 + 10    = 30
4th change: 30 + (-25) = 5

max(0, 10, 20, 30, 5) = 30

Example 3

Input: @gain = (3, -4, 2, 5, -6, 1)
Output: 6

start: 0
1st change:  0 + 3    = 3
2nd change:  3 + (-4) = -1
3rd change: -1 + 2    = 1
4th change:  1 + 5    = 6
5th change:  6 + (-6) = 0
6th change:  0 + 1    = 1

max(0, 3, -1, 1, 6, 0, 1) = 6

Example 4

Input: @gain = (-1, -2, -3, -4)
Output: 0

start: 0
1st change:  0 + (-1) = -1
2nd change: -1 + (-2) = -3
3rd change: -3 + (-3) = -6
4th change: -6 + (-4) = -10

max(0, -1, -3, -6, -10) = 0

Example 5

Input: @gain = (-10, 15, 5)
Output: 10

start: 0
1st change:   0 + (-10) = -10
2nd change: -10 + 15    = 5
3rd change:   5 + 5     = 10

max(0, -10, 5, 10) = 10

Solution

Taking the maximum of the cumulated sum over the numbers prepended by a zero.

use strict;
use warnings;
use PDL;

sub peak_point {
    max cumusumover pdl 0, @_;
}

See the full solution to task 2.


If you have a question about this post or if you like to comment on it, feel free to open an issue in my github repository.