aboutsummaryrefslogtreecommitdiff
path: root/challenge-172/james-smith/README.md
blob: 34c223f1e57275ad43656e1c60925d1abfae1a70 (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
[< Previous 171](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-171/james-smith) |
[Next 173 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-173/james-smith)

# The Weekly Challenge 172

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-172/james-smith

# Task 1 - Prime partition

***You are given two positive integers, `$m` and `$n`.  Write a script to find out the Prime Partition of the given number. No duplicates allowed.***

## Solution

The first thing to note that this is perfect case for a recursive solution.

If `$n` is one - then a partition exists iff `$m` is prime (We use `is_prime` from  `Math::Prime::Util` to check this. In this case we return `[$m]` or `()` respectively.

If not our first digit is going to be from `2` to the largest prime below `($m-($n-1)/2)/$n`. Again `Math::Prime::Util` has a method `primes` method for this. We then recurse with the `partition` function but this time we replace `$m` with `$m-$p`, `$n` with `$n-1` and `2` with the next prime after `$p`.

This gives us:

```perl
sub partition {
  my ( $m, $n, $p ) = ( @_, 0 );
    $n > 1
  ? map { $p = $_;
          map { [ $p, @{$_} ] } partition( $m-$p, $n-1, $p )
        } @{ primes $p+1, int( ( $m - $n/2 + 1/2 ) / $n ) }
  : $m > $p && is_prime $m ? [ $m ] : ();
}
```

**Note** we add a `0` to `@_` so that if (in the first call) `@_` only has two numbers `$p` is set to zero.

### Only one partition

If you only wish to get **a** partition then we simply tweak the code to have a return in the inner map - to return the entry if we find it!

```perl
sub first_partition {
  my ( $m, $n, $p ) = ( @_, 0 );
    $n > 1
  ? map { $p = $_;
          map { return [ $p, @{$_} ] } first_partition( $m-$p, $n-1, $p )
        } @{ primes $p+1, int( ( $m - $n/2 + 1/2 ) / $n ) }
  : $m > $p && is_prime $m ? [ $m ] : ();
}
```

# Task 2 - Five number summary

***You are given an array of integers.  Write a script to compute the five-number summary of the given set of integers. (min,lower-quartile,median,upper-quarile,max)***

## Solution

We will present three code solutions as the quartiles and median are not-uniquely defined in some cases.

 * `fivenum_range` - if the median/quartile falls between two entries {and the entries are different} - we return the two values as a "range"
 * `fivenum_med` -  if the median/quartile falls between two entries - then the average of the two values is used
 * `fivenum_avg` - if the median/quartile falls between two entries - then a weighted average is used. With more weight given to the point nearest the fraction { for median this will be the mid-point } but for the quartiles the weighting could be 1/4 : 3/4.

These each take a similar form.

  * Sort the values lowest to highest - the only real way to do this;
  * Then we run a series of maps.
     * Firstly get the index of the points
        * min is `0`;
        * max is `$N-1`;
        * the other three are distributed evenly between them
     * We then convert them to an integer index and:
        * the fractional part (`fivenum_avg`) OR
        * indicator whether there is a factional part (`fivenum_mid` and `fivenum_range`)
     * We then compute the value for that index
```perl
sub fivenum_avg {
  my @sort = sort { $a <=> $b } @_;                      # sort values
  [
    map { $_->[1]                                        # If lies between 2 points
        ? ( 1 - $_->[1] ) * $sort[ $_->[0]     ] +       # compute weighted average
                $_->[1]   * $sort[ $_->[0] + 1 ]
        : $sort[ $_->[0] ]                               # o/w return value
        }
    map { [ int $_, $_ - int $_ ] }                      # get LH-index, and distance of point from this
    map { $_/4*$#_ }                                     # calculate index
    0 .. 4
  ];
}

sub fivenum_mid {
  my @sort = sort { $a <=> $b } @_;                      # sort values
  [
    map { $_->[1]                                        # If lies between 2 points
        ? ($sort[$_->[0]] + $sort[$_->[0]+1])/2          # compute average
        : $sort[$_->[0]]                                 # o/w return value
        }                                      
    map { [ int $_, ($_ == int $_) ? 0 : 1 ] }           # get LH-index {and flag if point lies between 2 numbers}
    map { $_/4*$#_ }                                     # calculate index
    0 .. 4
  ];
}

sub fivenum_range {
  my @sort = sort { $a <=> $b } @_;                      # sort values
  [
    map { $_->[1] && $sort[$_->[0]] != $sort[$_->[0]+1]  # If lies between 2 points
        ? '<'.$sort[$_->[0]].'-'.$sort[$_->[0]+1].'>'    # return "range"
        : $sort[$_->[0]]                                 # o/w return value
        }
    map { [ int $_, ($_ == int $_) ? 0 : 1 ] }           # get LH-index {and flag if point lies between 2 numbers}
    map { $_/4*$#_ }                                     # calculate index
    0 .. 4
  ];
}
```