diff options
| author | Mohammad S Anwar <Mohammad.Anwar@yahoo.com> | 2022-07-25 01:21:29 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-25 01:21:29 +0100 |
| commit | 7f43710833ae0bcf6e2dc923378c0ae5df2934db (patch) | |
| tree | 60ac47f211a54629aac4d19f130da33e03ff6fca | |
| parent | 588154e83ece05031c0d2f613a66829138cb56e9 (diff) | |
| parent | 5badff2e0666b32a31c5704e6d54d1990bfcdad4 (diff) | |
| download | perlweeklychallenge-club-7f43710833ae0bcf6e2dc923378c0ae5df2934db.tar.gz perlweeklychallenge-club-7f43710833ae0bcf6e2dc923378c0ae5df2934db.tar.bz2 perlweeklychallenge-club-7f43710833ae0bcf6e2dc923378c0ae5df2934db.zip | |
Merge pull request #6494 from dcw803/master
solutions to ch#'174 plus belated attempts at ch#172 and 173
41 files changed, 2421 insertions, 57 deletions
diff --git a/challenge-172/duncan-c-white/C/Makefile b/challenge-172/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..c1c8318a24 --- /dev/null +++ b/challenge-172/duncan-c-white/C/Makefile @@ -0,0 +1,16 @@ +BUILD = ch-1 ch-2 +CC = gcc +CFLAGS = -Wall # -g +LDLIBS = -lm + +all: $(BUILD) + +clean: + /bin/rm -f $(BUILD) *.o core a.out + +primes.o: primes.c + +ch-1: ch-1.o primes.o args.o +ch-1.o: ch-1.c args.h +ch-2: ch-2.o args.o +ch-2.o: ch-2.c args.h diff --git a/challenge-172/duncan-c-white/C/README b/challenge-172/duncan-c-white/C/README new file mode 100644 index 0000000000..931c4d8879 --- /dev/null +++ b/challenge-172/duncan-c-white/C/README @@ -0,0 +1,7 @@ +Thought I'd also have a go at translating ch-1.pl and ch-2.pl into C.. + +Both C versions produce identical (non-debugging) output to my Perl originals + +ch-1.c uses primes.[ch], a prime number generator module that I wrote ages ago, +and I've finally split out the command-line argument processing code into +args.[ch]. diff --git a/challenge-172/duncan-c-white/C/args.c b/challenge-172/duncan-c-white/C/args.c new file mode 100644 index 0000000000..73a471464a --- /dev/null +++ b/challenge-172/duncan-c-white/C/args.c @@ -0,0 +1,136 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> + + +bool debug = false; + + +// process_flag_noarg( name, argc, argv ); +// Process the -d flag, and check that there are no +// remaining arguments. +void process_flag_noarg( char *name, int argc, char **argv ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 0 ) + { + fprintf( stderr, "Usage: %s [-d]\n", name ); + exit(1); + } +} + + +// process_onenumarg( name, argc, argv, &n ); +// Process the -d flag, and check that there is a single +// remaining numeric argument, putting it into n +void process_onenumarg( char *name, int argc, char **argv, int *n ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 1 ) + { + fprintf( stderr, "Usage: %s [-d] n\n", name ); + exit(1); + } + + // element is in argv[arg] + + if( debug ) + { + printf( "debug: remaining arguments are in arg=%d, " + "firstn=%s\n", arg, argv[arg] ); + } + + *n = atoi( argv[arg] ); +} + + +// process_twonumargs( name, argc, argv, &m, &n ); +// Process the -d flag, and check that there are 2 +// remaining numeric arguments, putting them into m and n +void process_twonumargs( char *name, int argc, char **argv, int *m, int *n ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left != 2 ) + { + fprintf( stderr, "Usage: %s [-d] m n\n", name ); + exit(1); + } + + // elements are in argv[arg] and argv[arg+1] + + if( debug ) + { + printf( "debug: remaining arguments are in arg=%d, " + "firstn=%s, secondn=%s\n", + arg, argv[arg], argv[arg+1] ); + } + + *m = atoi( argv[arg++] ); + *n = atoi( argv[arg] ); +} + +// int arr[100]; +// int nel = process_listnumargs( name, argc, argv, arr, 100 ); +// Process the -d flag, and check that there are >= 2 +// remaining numeric arguments, putting them into arr[0..nel-1] +// and returning nel. +int process_listnumargs( char *name, int argc, char **argv, int *arr, int maxel ) +{ + int arg=1; + if( argc>1 && strcmp( argv[arg], "-d" ) == 0 ) + { + debug = true; + arg++; + } + + int left = argc-arg; + if( left < 2 ) + { + fprintf( stderr, "Usage: %s [-d] list_of_numeric_args\n", name ); + exit(1); + } + if( left > maxel ) + { + fprintf( stderr, "%s: more than %d args\n", name, maxel ); + exit(1); + } + + // elements are in argv[arg], argv[arg+1]... + + if( debug ) + { + printf( "debug: remaining arguments are in arg=%d, " + "firstn=%s, secondn=%s..\n", + arg, argv[arg], argv[arg+1] ); + } + + int nel = 0; + for( int i=arg; i<argc; i++ ) + { + arr[nel++] = atoi( argv[i] ); + } + arr[nel] = -1; + return nel; +} diff --git a/challenge-172/duncan-c-white/C/args.h b/challenge-172/duncan-c-white/C/args.h new file mode 100644 index 0000000000..b3e4227183 --- /dev/null +++ b/challenge-172/duncan-c-white/C/args.h @@ -0,0 +1,6 @@ +extern bool debug; + +extern void process_flag_noarg( char * name, int argc, char ** argv ); +extern void process_onenumarg( char * name, int argc, char ** argv, int * n ); +extern void process_twonumargs( char * name, int argc, char ** argv, int * m, int * n ); +extern int process_listnumargs( char * name, int argc, char ** argv, int * arr, int maxel ); diff --git a/challenge-172/duncan-c-white/C/ch-1.c b/challenge-172/duncan-c-white/C/ch-1.c new file mode 100644 index 0000000000..95c43e5d3e --- /dev/null +++ b/challenge-172/duncan-c-white/C/ch-1.c @@ -0,0 +1,109 @@ +/* + * TASK 1 - Prime Partition + * + * GUEST LANGUAGE: THIS IS THE C VERSION OF ch-1.pl. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +//#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <ctype.h> + +#include "args.h" +#include "primes.h" + + + +#define MAXSTRINGLEN 800 +typedef char string[MAXSTRINGLEN]; + + +#define MAXPRIMES 512 + + +int main( int argc, char **argv ) +{ + int m, n; + process_twonumargs( "prime-partition", argc, argv, &m, &n ); + + // get all primes less than m + int numprimes; + int *primes = primes_upto( m, &numprimes ); + if( debug ) + { + printf( "found %d primes, %d, %d, %d...%d\n", + numprimes, primes[0], primes[1], primes[2], + primes[numprimes-1] ); + } + assert( numprimes < MAXPRIMES ); + + string pps = ""; // single string "pp1 or pp2 ..." + + int two2n = 1<<numprimes; + + for( int i=1; i<two2n; i++ ) + { + int chosen[MAXPRIMES]; + int nchosen = 0; + int sum = 0; + int mask = two2n>>1; + for( int pos=0; pos<numprimes; pos++ ) + { + if( (i&mask) ) + { + chosen[nchosen++] = primes[pos]; + sum += primes[pos]; + #if 0 + if( debug ) + { + printf( + "debug: i=%d, chosen %d, sum %d\n", + i, primes[pos], sum ); + } + #endif + } + mask >>= 1; + } + #if 0 + if( debug ) + { + printf( "debug: i=%d, nchosen=%d, sum=%d\n", + i, nchosen, sum ); + } + #endif + + if( sum == m && nchosen == n ) + { + string buf = ""; + char *p = buf; + for( int i=0; i<n; i++ ) + { + sprintf( p, "%d, ", chosen[i] ); + p += strlen(p); + } + p[strlen(p)-2] = '\0'; + if( debug ) + { + printf( "debug: i=%d: found a pp: %s\n", i, buf ); + } + int len = strlen(pps); + assert( len + strlen(buf) + 5 < MAXSTRINGLEN ); + strcat( pps, buf ); + strcat( pps, " or " ); + } + } + + // remove trailing " or ".. + int len = strlen(pps); + if( len>0 ) + { + pps[len-4] = '\0'; + } + printf( "%s\n", pps ); + + return 0; +} diff --git a/challenge-172/duncan-c-white/C/ch-2.c b/challenge-172/duncan-c-white/C/ch-2.c new file mode 100644 index 0000000000..86aea9c9f3 --- /dev/null +++ b/challenge-172/duncan-c-white/C/ch-2.c @@ -0,0 +1,108 @@ +/* + * Task 2 - Five-number Summary + * + * GUEST LANGUAGE: THIS IS THE C VERSION OF ch-2.pl. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <ctype.h> + +#include "args.h" + + +// +// double med = median( arr[], nel ); +// Calculate the median value in the sorted array arr[0..nel-1]. +// +double median( int *arr, int nel ) +{ + int midpos = nel/2; + double median = nel%2 == 0 ? + (arr[midpos-1]+arr[midpos])/2.0 : + arr[midpos]; + return median; +} + + +int intcompare( const void *p1, const void *p2 ) +{ + // The actual arguments to this function are "pointers to + // pointers to char", but strcmp(3) arguments are "pointers + // to char", hence the following cast plus dereference */ + + int a = *((int *)p1); + int b = *((int *)p2); + return a - b; +} + + +void prarr( int *arr, int nel ) +{ + for( int i=0; i<nel; i++ ) + { + printf( "%d, ", arr[i] ); + } + putchar('\n'); +} + + +#define MAXDATA 100 + +int main( int argc, char **argv ) +{ + int arr[MAXDATA]; + int nel = process_listnumargs( "five-number-summary", + argc, argv, arr, MAXDATA ); + + // sort arr[0..nel-1] numerically + qsort( arr, nel, sizeof(int), &intcompare ); + + if( debug ) + { + printf( "debug: sorted " ); + prarr( arr, nel ); + } + + double medn = median( arr, nel ); + + int midpos = nel/2; + if( debug ) + { + printf( "debug: median=%.2f, midpos=%d\n", medn, midpos ); + } + + // h1: first half of arr[] + int h1nel = midpos; + int *h1 = arr; + + // h2: second half of arr[] + if( nel%2 != 0 ) midpos++; + int *h2 = arr+midpos; + int h2nel = nel-midpos; + + if( debug ) + { + printf( "debug: h1 = " ); + prarr( h1, h1nel ); + printf( "debug: h2 = " ); + prarr( h2, h2nel ); + } + + double firstq = median( h1, h1nel ); + double thirdq = median( h2, h2nel ); + + double min = arr[0]; + double max = arr[nel-1]; + + printf( " %5.1f\n", medn ); + printf( "%5.1f %5.1f\n", firstq, thirdq ); + printf( "%5.1f %5.1f\n", min, max ); + + return 0; +} diff --git a/challenge-172/duncan-c-white/C/primes.c b/challenge-172/duncan-c-white/C/primes.c new file mode 100644 index 0000000000..9e9d0ba0fb --- /dev/null +++ b/challenge-172/duncan-c-white/C/primes.c @@ -0,0 +1,67 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <assert.h> +#include <math.h> + + +// int *primes = primes_upto(n,&numprimes); +// Generate a dynamic array of integers containing +// all primes up to $n. The array is zero-terminated. +// We also set numprimes to the number of primes found. +// The caller should free(primes) when they've finished +// using it. +// +int * primes_upto( int n, int *numprimep ) +{ + bool *isprime = (bool *) malloc( (n+1) * sizeof(bool) ); + assert( isprime != NULL ); + + int i; + for( i=1; i<=n; i++ ) + { + isprime[i] = 1; // initially + } + + int upper = (int)(sqrt((double)(n))); + //printf( "debug: n=%d, upper=%d\n", n, upper ); + for( i=2; i<=upper; i++ ) + { + if( isprime[i] ) + { + //printf( "debug: crossing out multiples of %d\n", i ); + int j; + for( j=i*i; j<=n; j+=i ) + { + isprime[j] = 0; + } + } + } + + // count how many primes there are + int np = 0; + for( i=2; i<=n; i++ ) + { + if( isprime[i] ) + { + np++; + } + } + *numprimep = np; + + // dynamically allocate an array of np+1 ints, and fill it in + int *primes = malloc( (np+1) * sizeof(int) ); + int p = 0; + for( i=2; i<=n; i++ ) + { + if( isprime[i] ) + { + primes[p++] = i; + } + } + primes[p] = 0; + + free( isprime ); + + return primes; +} diff --git a/challenge-172/duncan-c-white/C/primes.h b/challenge-172/duncan-c-white/C/primes.h new file mode 100644 index 0000000000..d9fd437aa7 --- /dev/null +++ b/challenge-172/duncan-c-white/C/primes.h @@ -0,0 +1 @@ +extern int * primes_upto( int n, int * numprimep ); diff --git a/challenge-172/duncan-c-white/README b/challenge-172/duncan-c-white/README index 755694b9fc..604f28c0b7 100644 --- a/challenge-172/duncan-c-white/README +++ b/challenge-172/duncan-c-white/README @@ -1,33 +1,38 @@ -Task 1: Abundant Number +Task 1: Prime Partition -Write a script to generate first 20 Abundant Odd Numbers. +You are given two positive integers, $m and $n. -According to wikipedia: A number n for which the sum of proper divisors -(divisors from 1 but less than n) s(n) > n. +Write a script to find out the Prime Partition of the given number $m that +contains $n elements. No duplicates allowed. -For example, 945 is the first Abundant Odd Number. +For example, -Sum of divisors: -1 + 3 + 5 + 7 + 9 + 15 + 21 + 27 + 35 + 45 + 63 + 105 + 135 + 189 + 315 = 975 + Input: $m = 18, $n = 2 + Output: 5, 13 or 7, 11 -MY NOTES: ok, sounds easy, and at least it's nowt to do with Primes:-) + Input: $m = 19, $n = 3 + Output: 3, 5, 11 + +MY NOTES: ok, sounds quite easy, even if it is to do with Primes. Without +duplicates means that each prime p < m is either present or absent in a +given partition, so we can use our old trick of having a counting loop from +0..2**(nitems)-1 and compute the sum of just those items selected by the +binary bits of the count that are 1 (then check that the sum is $m and +the number of elements being summed is $n) GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl into C (look in the C directory for that). -Task 2: First-class Function - -Create sub compose($f, $g) which takes in two parameters $f and $g as -subroutine refs and returns subroutine ref i.e. compose($f, $g)->($x) -= $f->($g->($x)) +Task 2: Five-number Summary -e.g. +You are given an array of integers. -$f = (one or more parameters function) -$g = (one or more parameters function) +Write a script to compute the five-number summary of the given set of integers. -$h = compose($f, $g) -$f->($g->($x,$y, ..)) == $h->($x, $y, ..) for any $x, $y, ... +You can find the definition and example in the wikipedia page: + https://en.wikipedia.org/wiki/Five-number_summary -MY NOTES: An interesting question at last. Think it's quite easy.. +MY NOTES: Nice and simple: sort the data, pick the median, first and third +quartile values. Note that the median can be the average (mean) of the +two central values if the #values is even. diff --git a/challenge-172/duncan-c-white/perl/MakePrimes.pm b/challenge-172/duncan-c-white/perl/MakePrimes.pm new file mode 100644 index 0000000000..e268fa06a3 --- /dev/null +++ b/challenge-172/duncan-c-white/perl/MakePrimes.pm @@ -0,0 +1,90 @@ +# +# mkprimes module (converted from mkprimes.c) +# + +use strict; +use warnings; +use Function::Parameters; + +my $debug = 0; +my @foundprimes; # remember all primes we've found.. + + +# +# my @primes = primes_upto( $n ); +# Find all primes up to N; return a list of all such primes +# (note that 1 is not usually considered a prime) +# +fun primes_upto( $n ) +{ + my @isprime; + + for( my $i=1; $i<=$n; $i++ ) + { + $isprime[$i] = 1; # initially + } + + # now sieve the non-primes out.. + my $upper = int(sqrt($n)); + printf( "debug: n=%d, upper=%d\n", $n, $upper ) if $debug; + for( my $i=2; $i<=$upper; $i++ ) + { + if( $isprime[$i] ) + { + #printf( "debug: crossing out multiples of %d\n", $i ); + for( my $j=$i*$i; $j<=$n; $j+=$i ) + { + $isprime[$j] = 0; + } + } + } + + # after sieving, extract the primes + my @primes = grep { $isprime[$_] } 2..$n; + + # remember them + @foundprimes = @primes; + + return @primes; +} + + +# +# my @moreprimes = more_primes( $n, $m ); +# Need more primes! Have @foundprimes up to $n, but need +# to sieve primes from $n+1..$m, so re-sieve, return +# a list of all new primes (in the range $n+1..$m) that we find. +# +fun more_primes( $n, $m ) +{ + my %isprime; + + print "finding more primes from ", $n+1, "..$m\n"; + + for( my $i=$n+1; $i<=$m; $i++ ) + { + $isprime{$i} = 1; # pre-sieving + } + + # now sieve the non-primes out.. + foreach my $prime (@foundprimes) + { + # find first multiple of $prime > $n + my $mult = $prime * (int($n/$prime)+1); + + #print "debug: xo multiples of $prime from $mult to $m\n"; + + for( my $j=$mult; $j<=$m; $j+=$prime ) + { + delete $isprime{$j}; + } + } + + # after sieving, extract the primes + my @primes = grep { $isprime{$_} } $n+1..$m; + push @foundprimes, @primes; + return @primes; +} + + +1; diff --git a/challenge-172/duncan-c-white/perl/ch-1.pl b/challenge-172/duncan-c-white/perl/ch-1.pl new file mode 100755 index 0000000000..d2b3daf950 --- /dev/null +++ b/challenge-172/duncan-c-white/perl/ch-1.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl +# +# 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 $m that +# contains $n elements. No duplicates allowed. +# +# For example, +# +# Input: $m = 18, $n = 2 +# Output: 5, 13 or 7, 11 +# +# Input: $m = 19, $n = 3 +# Output: 3, 5, 11 +# +# MY NOTES: ok, sounds quite easy, even if it is to do with Primes. Without +# duplicates means that each prime p < m is either present or absent in a +# given partition, so we can use our old trick of having a counting loop from +# 0..2**(nitems)-1 and compute the sum of just those items selected by the +# binary bits of the count that are 1 (then check that the sum is $m and +# the number of elements being summed is $n) +# +# GUEST LANGUAGE: As a bonus, I also had a go at translating ch-1.pl +# into C (look in the C directory for that). +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Data::Dumper; +use List::Util qw(sum0); + +use lib qw(.); +use MakePrimes; + +my $debug=0; +die "Usage: prime-partition [--debug] M N\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV==2; +my( $m, $n ) = @ARGV; + +my @primes = primes_upto( $m-1 ); +my $nitems = @primes; + +my @pp; # array of prime partitions, each a string + +my $two2n = 2**$nitems; + +for( my $i=0; $i<$two2n; $i++ ) +{ + my $b = sprintf("%0${nitems}b", $i ); + my @select = split( //, $b ); + + my @chosen = map { $primes[$_] } grep { $select[$_] } 0..$nitems-1; + my $sum = sum0(@chosen); + #say "$i: b=$b, select=", join(',',@select), + # ", chosen=", join(',',@chosen), + # ", sum=$sum" if $debug; + next unless $sum == $m; + next unless @chosen == $n; + say "$i: b=$b, found a pp: ", join(',',@chosen) if $debug; + push @pp, join( ', ', @chosen); +} + +my $str = join( ' or ', @pp ); +say $str; diff --git a/challenge-172/duncan-c-white/perl/ch-2.pl b/challenge-172/duncan-c-white/perl/ch-2.pl new file mode 100755 index 0000000000..06618cae8d --- /dev/null +++ b/challenge-172/duncan-c-white/perl/ch-2.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl +# +# 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. +# You can find the definition and example in the wikipedia page: +# https://en.wikipedia.org/wiki/Five-number_summary +# +# MY NOTES: Nice and simple: sort the data, pick the median, first and third +# quartile values. Note that the median can be the average (mean) of the +# two central values if the #values is even. +# + +use strict; +use warnings; +use feature 'say'; +use Getopt::Long; +use Data::Dumper; + +my $debug=0; +die "Usage: five-number-summary [--debug] list_of_ints\n" + unless GetOptions( "debug"=>\$debug ) && @ARGV>1; + +my @n = sort { $a <=> $b } @ARGV; +say "debug: sorted ", join(',', @n ) if $debug; + + +# +# my $median = median( @n ); +# Calculate the median value in the sorted array @n. +# +sub median +{ + my( @n ) = @_; + my $midpos = int(@n/2); + my $median = @n%2 == 0 ? ($n[$midpos-1]+$n[$midpos])/2 : $n[$midpos]; + return $median; +} + + +my $median = median( @n ); + +my $midpos = int(@n/2); +say "debug: midpos=$midpos" if $debug; + +my @h1 = @n[0..$midpos-1]; + +$midpos++ if @n%2 != 0; +my @h2 = @n[$midpos..@n-1]; + +say "debug: h1 = ", join(',',@h1), ", h2 = ", join(',',@h2) + if $debug; + +my $firstq = median( @h1 ); +my $thirdq = median( @h2 ); + +my $min = $n[0]; +my $max = $n[-1]; + +printf " %5.1f\n", $median; +printf "%5.1f %5.1f\n", $firstq, $thirdq; +printf "%5.1f %5.1f\n", $min, $max; diff --git a/challenge-173/duncan-c-white/C/.cbuild b/challenge-173/duncan-c-white/C/.cbuild index 7171ba4c65..fce622d376 100644 --- a/challenge-173/duncan-c-white/C/.cbuild +++ b/challenge-173/duncan-c-white/C/.cbuild @@ -1,3 +1,4 @@ -BUILD = ch-1 +BUILD = ch-1 ch-2 CFLAGS = -Wall +LDFLAGS = -lgmp #CFLAGS = -g diff --git a/challenge-173/duncan-c-white/C/Makefile b/challenge-173/duncan-c-white/C/Makefile new file mode 100644 index 0000000000..9d1b99a602 --- /dev/null +++ b/challenge-173/duncan-c-white/C/Makefile @@ -0,0 +1,14 @@ +BUILD = ch-1 ch-2 +CC = gcc +CFLAGS = -Wall -g +LDLIBS = -lm -lgmp + +all: $(BUILD) + |
