This is my response to The Weekly Challenge #339.
(a, b)
and (c, d)
, the product difference is
(a * b) - (c * d)
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)
#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems >= 4 && all(@ints) ~~ Int, # [1]
:v(:$verbose));
my $max = -Inf; # [2]
for @ints.combinations(4) -> @comb # [3]
{
for (@comb.permutations)[0,2,4] -> @perm # [4]
{
my $diff = (( @perm[0] * @perm[1] ) - ( @perm[2] * @perm[3] )).abs; # [5]
print ": (@perm[0] * @perm[1]) - (@perm[2] * @perm[3]) = $diff" if
$verbose;
if ($diff > $max) # [6]
{
$max = $diff; # [6a]
say " (new max)" if $verbose;
}
elsif $verbose
{
say '';
}
}
}
say $max; # [7]
[1] At least 4 elements, all of which must be integers.
[2] Initialise the max difference, starting off with something that is less than everything else we can find.
[3] Iterate over all four value combinations of
the input values with combinations(4)
.
> say <1 2 3 4 5>.combinations(4);
((1 2 3 4) (1 2 3 5) (1 2 4 5) (1 3 4 5) (2 3 4 5))
See docs.raku.org/routine/combinations for more information about combinations
.
[4] We have to shuffle around the values as well, with
permutations
. But we do not need all the 24 permutations; just three
of them is enough - so that we can combine the first value with the other
three.
See docs.raku.org/routine/permutations for more information about permutations
.
The internal order of the first and second value, as well as the (internal
value of the) third and fourth, does not matter; as a * b
b * a
|ab - cd|
|cd - ab|
> say <1 2 3 4>.permutations;
((1 2 3 4) (1 2 4 3) (1 3 2 4) (1 3 4 2) (1 4 2 3) (1 4 3 2) \
(2 1 3 4) (2 1 4 3) (2 3 1 4) (2 3 4 1) (2 4 1 3) (2 4 3 1) \
(3 1 2 4) (3 1 4 2) (3 2 1 4) (3 2 4 1) (3 4 1 2) (3 4 2 1) \
(4 1 2 3) (4 1 3 2) (4 2 1 3) (4 2 3 1) (4 3 1 2) (4 3 2 1))
[5] Get the difference of the current permutation. Note the use of abs
to get rid of a negative sign, if any.
See docs.raku.org/routine/abs for more information about abs
.
[6] Is the new difference higher than the previously highest one? If so, save the new value [6a].
[7] Print the result.
Running it:
$ ./max-diff 5 9 3 4 6
42
$ ./max-diff 1 -2 3 -4
10
$ ./max-diff -- -3 -1 -2 -4
10
$ ./max-diff 10 2 0 5 1
50
$ ./max-diff 7 8 9 10 10
44
Looking good.
With verbose mode:
$ ./max-diff -v 5 9 3 4 6
: (5 * 9) - (3 * 4) = 33 (new max)
: (5 * 3) - (9 * 4) = 21
: (5 * 4) - (9 * 3) = 7
: (5 * 9) - (3 * 6) = 27
: (5 * 3) - (9 * 6) = 39 (new max)
: (5 * 6) - (9 * 3) = 3
: (5 * 9) - (4 * 6) = 21
: (5 * 4) - (9 * 6) = 34
: (5 * 6) - (9 * 4) = 6
: (5 * 3) - (4 * 6) = 9
: (5 * 4) - (3 * 6) = 2
: (5 * 6) - (3 * 4) = 18
: (9 * 3) - (4 * 6) = 3
: (9 * 4) - (3 * 6) = 18
: (9 * 6) - (3 * 4) = 42 (new max)
42
$ ./max-diff -v 1 -2 3 -4
: (1 * -2) - (3 * -4) = 10 (new max)
: (1 * 3) - (-2 * -4) = 5
: (1 * -4) - (-2 * 3) = 2
10
$ ./max-diff -v -- -3 -1 -2 -4
: (-3 * -1) - (-2 * -4) = 5 (new max)
: (-3 * -2) - (-1 * -4) = 2
: (-3 * -4) - (-1 * -2) = 10 (new max)
10
$ ./max-diff -v 10 2 0 5 1
: (10 * 2) - (0 * 5) = 20 (new max)
: (10 * 0) - (2 * 5) = 10
: (10 * 5) - (2 * 0) = 50 (new max)
: (10 * 2) - (0 * 1) = 20
: (10 * 0) - (2 * 1) = 2
: (10 * 1) - (2 * 0) = 10
: (10 * 2) - (5 * 1) = 15
: (10 * 5) - (2 * 1) = 48
: (10 * 1) - (2 * 5) = 0
: (10 * 0) - (5 * 1) = 5
: (10 * 5) - (0 * 1) = 50
: (10 * 1) - (0 * 5) = 10
: (2 * 0) - (5 * 1) = 5
: (2 * 5) - (0 * 1) = 10
: (2 * 1) - (0 * 5) = 2
50
$ ./max-diff -v 7 8 9 10 10
: (7 * 8) - (9 * 10) = 34 (new max)
: (7 * 9) - (8 * 10) = 17
: (7 * 10) - (8 * 9) = 2
: (7 * 8) - (9 * 10) = 34
: (7 * 9) - (8 * 10) = 17
: (7 * 10) - (8 * 9) = 2
: (7 * 8) - (10 * 10) = 44 (new max)
: (7 * 10) - (8 * 10) = 10
: (7 * 10) - (8 * 10) = 10
: (7 * 9) - (10 * 10) = 37
: (7 * 10) - (9 * 10) = 20
: (7 * 10) - (9 * 10) = 20
: (8 * 9) - (10 * 10) = 28
: (8 * 10) - (9 * 10) = 10
: (8 * 10) - (9 * 10) = 10
44
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
File: peak-point
#! /usr/bin/env raku
unit sub MAIN (*@gain where @gain.elems >= 1 && all(@gain) ~~ Int, # [1]
:v(:$verbose));
my $max = 0; # [2]
my $current = 0; # [3]
say ": Start at 0 (new max)" if $verbose;
for @gain -> $change # [4]
{
print ": $current + $change = { $current + $change }" if $verbose;
$current += $change; # [5]
if ($current > $max) # [6]
{
say " (new max)" if $verbose;
$max = $current; # [6a]
}
elsif $verbose
{
say '';
}
}
say $max; # [7]
[1] At least one element, all of which must be integers. Note that the challenge does not specify integer input, but the examples imply it.
[2] The initial maximum, where we start.
[3] The current altitude, updated as we iterate over the gains.
[4] Iterate over the gains.
[5] Add (or subtract) the current gain to the current altitude.
[6] Do we have a new maximum altitude? If so, set is as the new maximum [6a].
[7] Print the result.
Running it:
$ ./peak-point -- -5 1 5 -9 2
1
$ ./peak-point 10 10 10 -25
30
$ ./peak-point 3 -4 2 5 -6 1
6
$ ./peak-point -- -1 -2 -3 -4
0
$ ./peak-point -- -10 15 5
10
Looking good.
With verbose mode:
$ ./peak-point -v -- -5 1 5 -9 2
: Start at 0 (new max)
: 0 + -5 = -5 (new max)
: -5 + 1 = -4 (new max)
: -4 + 5 = 1 (new max)
: 1 + -9 = -8
: -8 + 2 = -6
1
$ ./peak-point -v 10 10 10 -25
: Start at 0 (new max)
: 0 + 10 = 10 (new max)
: 10 + 10 = 20 (new max)
: 20 + 10 = 30 (new max)
: 30 + -25 = 5
30
$ ./peak-point -v 3 -4 2 5 -6 1
: Start at 0 (new max)
: 0 + 3 = 3 (new max)
: 3 + -4 = -1
: -1 + 2 = 1
: 1 + 5 = 6 (new max)
: 6 + -6 = 0
: 0 + 1 = 1
6
$ ./peak-point -v -- -1 -2 -3 -4
: Start at 0 (new max)
: 0 + -1 = -1
: -1 + -2 = -3
: -3 + -3 = -6
: -6 + -4 = -10
0
$ ./peak-point -v -- -10 15 5
: Start at 0 (new max)
: 0 + -10 = -10
: -10 + 15 = 5 (new max)
: 5 + 5 = 10 (new max)
10
And that's it.