aboutsummaryrefslogtreecommitdiff
path: root/challenge-206/james-smith/README.md
blob: 4c0ad820a202e5eada53e90886e8f9dfd40acb61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
[< Previous 205](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-205/james-smith) |
[Next 207 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-207/james-smith)

# The Weekly Challenge 206

You can find more information about this weeks, and previous weeks challenges at:

  https://theweeklychallenge.org/

If you are not already doing the challenge - it is a good place to practise your
**perl** or **raku**. If it is not **perl** or **raku** you develop in - you can
submit solutions in whichever language you feel comfortable with.

You can find the solutions here on github at:

https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-2065/james-smith

# Task 1: Shortest Time

***You are given a list of time points, at least 2, in the 24-hour clock format `HH:MM`. Write a script to find out the shortest time in minutes between any two time points.***

## Solution

We will do a pairwise comparison of each pair. The shortest time for any pair is either going from the absolute differences in times directly - OR going through midnight. These are `abs( t1 - t2 )` or `abs( t1 + t2 - 1440 )`. The code becomes:

```perl
sub shortest_time {
  my $min = 1_440, @_ = map { @Q = split /:/; $Q[0]*60 + $Q[1] } @_;
  while( defined (my $t = shift) ) {
    abs( $t-$_       ) < $min && ( $min = abs $t-$_       ),
    abs( $t+$_-1_440 ) < $min && ( $min = abs $t+$_-1_440 ) for @_;
  }
  $min
}
```

Now how efficient is this - though - is there a better way to use built-in perk functions?

If we sort the times in order, we only have to compare the `n` gaps, from the last to the first through midnight and each of the subsequent neighbours.

This gives us two alternative code blocks:

```perl
sub shortest_time {
  @_ = map { my @Q = split /:/; $Q[0]*60 + $Q[1] } sort @_;
  my $min = 1440 + (my $t = shift) - $_[-1];
  ($_-$t<$min) && ($min=$_-$t), $t=$_ for @_;
  $min
}
```

or:

```perl
sub shortest_time {
  @_ = sort { $a<=>$b } map { my @Q = split /:/; $Q[0]*60 + $Q[1] } @_;
  my $min = 1440 + (my $t = shift) - $_[-1];
  ($_-$t<$min) && ($min=$_-$t), $t=$_ for @_;
  $min
}
```

Which of these is fastest? The `sort` method is much more efficient than the pairwise approach (It's `O(n.log n)` where the pairwise solution is `O(n^2)`. Of the two the second numeric `sort` after the `map` if slightly faster than the `map` after the string `sort`.

# Task 2: Array Pairings

***You are given an array of integers having even number of elements. Write a script to find the maximum sum of the minimum of each pairs.***

## Solution

There is a trick here - the optimal solution is achieved by sorting the array into order and then chunking into to pairs... Then take the minimum of each...

```perl
sub max_sum_pair_min {
  my $t = 0, @_ = sort {$a<=>$b} @_;
  $t += shift, shift while @_;
  $t
}
```

When we `shift`, `shift` the first value is added to the total, the second value is discarded.