From 7d1248f1c265713a3bf41dfab4492e9fa24d57cb Mon Sep 17 00:00:00 2001 From: dcw Date: Mon, 9 Sep 2019 03:05:18 +0100 Subject: 3 hours too late, finished writing the indexer ch-2.pl --- challenge-024/duncan-c-white/README | 55 ++++++----- challenge-024/duncan-c-white/perl5/ch-1.sh | 11 +++ challenge-024/duncan-c-white/perl5/ch-2.pl | 115 ++++++++++++++++++++++ challenge-024/duncan-c-white/perl5/docs/times-001 | 4 + challenge-024/duncan-c-white/perl5/docs/times-002 | 7 ++ challenge-024/duncan-c-white/perl5/docs/times-003 | 4 + challenge-024/duncan-c-white/perl5/docs/times-004 | 10 ++ challenge-024/duncan-c-white/perl5/docs/times-005 | 4 + challenge-024/duncan-c-white/perl5/docs/times-006 | 6 ++ challenge-024/duncan-c-white/perl5/docs/times-007 | 7 ++ challenge-024/duncan-c-white/perl5/docs/times-008 | 6 ++ challenge-024/duncan-c-white/perl5/docs/times-009 | 8 ++ challenge-024/duncan-c-white/perl5/docs/times-010 | 11 +++ challenge-024/duncan-c-white/perl5/docs/times-011 | 6 ++ 14 files changed, 227 insertions(+), 27 deletions(-) create mode 100755 challenge-024/duncan-c-white/perl5/ch-1.sh create mode 100755 challenge-024/duncan-c-white/perl5/ch-2.pl create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-001 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-002 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-003 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-004 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-005 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-006 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-007 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-008 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-009 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-010 create mode 100644 challenge-024/duncan-c-white/perl5/docs/times-011 diff --git a/challenge-024/duncan-c-white/README b/challenge-024/duncan-c-white/README index 4cd2bbfe01..926e2938aa 100644 --- a/challenge-024/duncan-c-white/README +++ b/challenge-024/duncan-c-white/README @@ -1,36 +1,37 @@ -Challenge 1: "Create a script that prints nth order forward difference -series. You should be a able to pass the list of numbers and order number -as command line parameters. Let me show you with an example: +Challenge 1: "Create a smallest script in terms of size that on +execution doesn't throw any error. The script doesn't have to do anything +special. You could even come up with smallest one-liner." -Suppose we have list (X) of numbers: 5, 9, 2, 8, 1, 6 and we would like -to create 1st order forward difference series (Y). So using the formula -Y(i) = X(i+1) - X(i), we get the following numbers: (9-5), (2-9), (8-2), -(1-8), (6-1), ie 4, -7, 6, -7, 5. -If you noticed, it has one less number than the original series. -Similarly you can generate the 2nd order forward difference series like: -(-7-4), (6+7), (-7-6), (5+7) => -11, 13, -13, 12. +My notes: Umm, if it doesn't have to do anything special, and we want it to +be tiny, does it have to do anything at all? Why not write the shortest +Perl one-liner: perl -e 1:-) -My notes: Clearly defined, very easy - let's have a go.. +Challenge 2: "Create a script to implement full text search functionality +using Inverted Index. According to wikipedia: -Challenge 2: "Create a script that prints Prime Decomposition of a -given number. The prime decomposition of a number is defined as a list -of prime numbers which when all multiplied together, are equal to that -number. For example, the Prime decomposition of 228 is 2,2,3,19 as 228 = -2 * 2 * 3 * 19." +In computer science, an inverted index (also referred to as a +postings file or inverted file) is a database index storing +a mapping from content, such as words or numbers, to its +locations in a table, or in a document or a set of documents +(named in contrast to a forward index, which maps from documents +to content). The purpose of an inverted index is to allow fast +full-text searches, at a cost of increased processing when a +document is added to the database." -My notes: So, prime factors then. Very easy again. In fact, haven't I -already solved this in one of the other prime-based questions? +My notes: One extreme to the other, an inverted index might be quite a +lot of work. Most especially, it would need an index-creator/updater +and a search-using-index tool. Also, the wikipedia article says that +some inverted indexes are: +wordindocument: word -> set of document (names or numbers), -Challenge 3: "Write a script to use Random Poems API: -https://www.poemist.com/api/v1/randompoems -This is the easiset API, I have come across so far. You don't need API -key for this. They have only route to work with (GET). The API task is -optional but we would love to see your solution." +whereas others are: -My notes: ok, even I can't argue that obtaining an API key for an API -I will literally never use again is too much hassle - when I don't need -an API key, and the whole program appears to be an LWP::Simple get.. +wordwhereindocuments: word -> set of (document, position) -update: well, apart from the Unicode in the response, complicating life. +(or perhaps set of word -> set of document -> list of position) + +Knowing the positions of each word in each document allows us to +search for several words "near to each other", so that's very useful. +But does the question want us to do that or not? Minimalism says not:-) diff --git a/challenge-024/duncan-c-white/perl5/ch-1.sh b/challenge-024/duncan-c-white/perl5/ch-1.sh new file mode 100755 index 0000000000..37c2dec647 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/ch-1.sh @@ -0,0 +1,11 @@ +#!/bin/sh - +# +# Challenge 1: "Create a smallest script in terms of size that on +# execution doesn't throw any error. The script doesn't have to do anything +# special. You could even come up with smallest one-liner." +# +# My notes: Umm, if it doesn't have to do anything special, and we want it to +# be tiny, does it have to do anything at all? Why not write the shortest +# Perl one-liner: perl -e 1:-) +# +perl -e 1 diff --git a/challenge-024/duncan-c-white/perl5/ch-2.pl b/challenge-024/duncan-c-white/perl5/ch-2.pl new file mode 100755 index 0000000000..47ea06621e --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/ch-2.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl +# +# Challenge 2: "Create a script to implement full text search functionality +# using Inverted Index.. a database index storing a mapping from content, +# such as words or numbers, to its locations in a set of documents. +# The purpose of an inverted index is to allow fast full-text searches, +# at a cost of increased processing when a document is added to the database." +# +# My notes: One extreme to the other, an inverted index might be quite a +# lot of work. It needs an index-creator/updater mode, and a +# search-using-index mode. +# Let's use a dbm file to store the index, specifically: +# +# wordindocument: word -> set of document names, stored as a sorted list +# + +use v5.10; # for "say" +use strict; +use warnings; +use Function::Parameters; +use DB_File; +use Data::Dumper; + +my $usage = "Usage: ch-2.pl i[ndex] DOCUMENT[s],\n". + "or: ch-2.pl s[earch] keywords\n". + "or: ch-2.pl l[ist]\n"; +die $usage unless @ARGV > 0; + +my %index; +tie %index, 'DB_File', "index" || die "ch-2.pl: can't tie to index\n"; + +my $mode = shift @ARGV; +if( $mode =~ /^i/i ) +{ + die $usage unless @ARGV; + add( @ARGV ); +} elsif( $mode =~ /^s/i ) +{ + die $usage unless @ARGV; + my %docs = search( @ARGV ); + my $docstr = join( ',', sort keys %docs ); + print "documents containing @ARGV: $docstr\n"; +} elsif( $mode =~ /^l/i ) +{ + while( my $w = each %index ) + { + print "$w: $index{$w}\n"; + } +} else +{ + die $usage; +} +untie %index; + + +# +# addwordtoindex( $w, $filename ); +# Word $w occurs in file $filename, add this to the index. +# +fun addwordtoindex( $w, $filename ) +{ + my $docset = $index{$w} // ""; + my %s = map { $_ => 1 } split( /,/, $docset ); + $s{$filename}++; + $index{$w} = join(',', sort keys %s); +} + + +# +# add( @filenames ); +# Add each document named in @filename to the index. +# +fun add( @filename ) +{ + foreach my $file (@filename) + { + open( my $infh, '<', $file ) || next; + my %set; # words in this file + while( <$infh> ) + { + chomp; + my @wd = split( /\s+/ ); + foreach my $w (@wd) + { + $w =~ s/[\.,;:!?"'({\[]+$//; + next if $set{$w}++; + addwordtoindex( $w, $file ); + } + } + close( $infh ); + } +} + +# +# my %docs = search( @word ); +# Search for documents containing all the words in @word. +# using set intersection.. return a document set. +# +fun search( @word ) +{ + @word = grep { defined $index{$_} } @word; + my $w = shift @word; + my %docset = map { $_ => 1 } split(/,/, $index{$w}); + print "$w in $index{$w}\n"; + foreach my $w (@word) + { + my %set2 = map { $_ => 1 } split(/,/, $index{$w}); + print "$w in $index{$w}\n"; + foreach my $w (keys %docset) + { + delete $docset{$w} unless $set2{$w}; + } + } + return %docset; +} diff --git a/challenge-024/duncan-c-white/perl5/docs/times-001 b/challenge-024/duncan-c-white/perl5/docs/times-001 new file mode 100644 index 0000000000..722d22229a --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-001 @@ -0,0 +1,4 @@ +What do I mean about honesty and logic? The frontrunner in the race +for Downing Street offered a masterclass in his lack of it during the +referendum: "My policy on cake is pro having it and pro eating it." +Boris: You can't. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-002 b/challenge-024/duncan-c-white/perl5/docs/times-002 new file mode 100644 index 0000000000..75366081a0 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-002 @@ -0,0 +1,7 @@ +The Archangel Gabriel couldn't have delivered Mrs May's famous +"Brexit that works for everyone" promise. It will become fashionable +in columns like these to identify things she could have done to get +her deal through, and the time (always yesterday) when she could have +done them. And I can believe that with Mr Gove's persuasiveness or Mr +Johnson's amiable bombast, a different prime minister might just have +pushed something like her deal over the line. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-003 b/challenge-024/duncan-c-white/perl5/docs/times-003 new file mode 100644 index 0000000000..8dffb9be2a --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-003 @@ -0,0 +1,4 @@ +But - have we all forgotten? - her deal is for the 22-month transition +period, not for Britain's final status outside the EU. So we'd now +be in that transition period, still tearing ourselves apart, for it's +really only about the final status that Brexiteers and Remainers disagree. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-004 b/challenge-024/duncan-c-white/perl5/docs/times-004 new file mode 100644 index 0000000000..1d8b2a5356 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-004 @@ -0,0 +1,10 @@ +And so to the logic. It's possible to believe (as I don't) that Brexit +could lead us to glory: but only after a "clean" exit from the EU +and the ties that come with membership. And it's possible to believe +(as I do) that we are wiser to remain. But to believe we could benefit +from being half-in, half-out defies logic. The ties of membership, +or half-membership, are what real Leavers believe hold us back. Real +Remainers, meanwhile, share their horror at subjecting ourselves to +rules we've lost the right to shape. The illogic of compromise that +delivers the worst of both worlds would defeat Gabriel, defeated Mrs May, +and will defeat whoever succeeds her. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-005 b/challenge-024/duncan-c-white/perl5/docs/times-005 new file mode 100644 index 0000000000..a8202dd0c2 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-005 @@ -0,0 +1,4 @@ +And so to honesty. Somebody has to square with the British people. She +never would. It is about Remain or Leave. We loop back to 2016, but this +time with a much clearer grasp of what "Leave" means. Isn't the +Gordian knot cut by putting the question again? diff --git a/challenge-024/duncan-c-white/perl5/docs/times-006 b/challenge-024/duncan-c-white/perl5/docs/times-006 new file mode 100644 index 0000000000..84e60d366b --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-006 @@ -0,0 +1,6 @@ +And here, I don't mean to queer Mr Johnson's pitch by putting the wind +up his Brexiteer supporters, but must mention one faint hope: a reason +for hoping a Johnson premiership would not end in calamity. My Times +colleague Rachel Sylvester discussed it in these pages on Tuesday. Mr +Johnson might be capable of ratting on his promise to take us out of +the EU - and getting away with it. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-007 b/challenge-024/duncan-c-white/perl5/docs/times-007 new file mode 100644 index 0000000000..6aff7a66c7 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-007 @@ -0,0 +1,7 @@ +The arguments against his suitability are too many for a comprehensive +list. Casual disregard for the truth; reckless caprice; lazy disregard for +detail; weak negotiating skills (as Whitehall knows); moral turpitude +which perhaps we should overlook in politics but which has been so +destructive of others' lives that I cannot forget it; and his failure +as foreign secretary to achieve anything but an extension of his notoriety +beyond our own shores. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-008 b/challenge-024/duncan-c-white/perl5/docs/times-008 new file mode 100644 index 0000000000..47e3a38702 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-008 @@ -0,0 +1,6 @@ +The man's a rascal. But like many rascals he's capable of a big +decision. It's possible to imagine him telling the country that this +Brexit business has got into such a poisonous muddle that we need to rip +it up and start again: to revoke Article 50, or refer back to the people, +or both. He might escape with his life. A Hunt, a Gove, a Hancock or a +Javid wouldn't. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-009 b/challenge-024/duncan-c-white/perl5/docs/times-009 new file mode 100644 index 0000000000..e1aa54a521 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-009 @@ -0,0 +1,8 @@ +Be clear: whoever takes over will soon enough need to be very, very +bold, one way or the other. Would-be Tory leaders will shortly be wooing +supporters with a promise to "go back to Brussels" for a better deal, +threatening no-deal Brexit if they don't. Whoever wins will then have +to try. They'll return empty-handed. What then? Here's Mr Johnson, +speaking in Switzerland today: "We will leave the EU on October 31, +deal or no deal ... The way to get a good deal is to prepare for a no +deal. To get things done you need to be prepared to walk away." diff --git a/challenge-024/duncan-c-white/perl5/docs/times-010 b/challenge-024/duncan-c-white/perl5/docs/times-010 new file mode 100644 index 0000000000..6baa07a886 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-010 @@ -0,0 +1,11 @@ +This week the Institute for Government published an important report, +suggesting that a PM intent on a no-deal Brexit could thwart parliament by +a lightning decision to do it without MPs' say-so. Be warned, would-be +prime ministers: this would be nuclear, a coup against representative +democracy and a breach of our unwritten constitution. This way, infamy +lies. Gangrene would follow such an amputation. Don't even think +about it. + +That leaves a referendum, a revocation, a general election, or all +three. Theresa May's departing tears are unlikely to be the last shed +at Downing Street's door. diff --git a/challenge-024/duncan-c-white/perl5/docs/times-011 b/challenge-024/duncan-c-white/perl5/docs/times-011 new file mode 100644 index 0000000000..0103b56f15 --- /dev/null +++ b/challenge-024/duncan-c-white/perl5/docs/times-011 @@ -0,0 +1,6 @@ +Boris Johnson is enough of a rascal to rat on Brexit + +Matthew Parris + +The frontrunner for No 10 might be the only candidate who'd get away +with ripping up Article 50 and starting again. -- cgit From b809d0e1f86602649b6468d305a2023c5c9571f8 Mon Sep 17 00:00:00 2001 From: dcw Date: Sun, 22 Sep 2019 23:45:53 +0100 Subject: incorporated long overdue challenge-025 and new 026 --- challenge-025/duncan-c-white/README | 52 ++-- challenge-025/duncan-c-white/blog.txt | 1 + challenge-025/duncan-c-white/perl5/ch-1.pl | 212 ++++++++++++++++ challenge-025/duncan-c-white/perl5/v0.pl | 44 ++++ challenge-025/duncan-c-white/perl5/v1.pl | 102 ++++++++ challenge-025/duncan-c-white/perl5/v15.pl | 224 +++++++++++++++++ .../duncan-c-white/perl5/v16-with-histo.pl | 278 +++++++++++++++++++++ challenge-025/duncan-c-white/perl5/v16.pl | 227 +++++++++++++++++ challenge-025/duncan-c-white/perl5/v17.pl | 220 ++++++++++++++++ challenge-025/duncan-c-white/perl5/v18.pl | 219 ++++++++++++++++ challenge-025/duncan-c-white/perl5/v19.pl | 214 ++++++++++++++++ challenge-025/duncan-c-white/perl5/v2.pl | 106 ++++++++ challenge-025/duncan-c-white/perl5/v20.pl | 212 ++++++++++++++++ challenge-025/duncan-c-white/perl5/v3.pl | 108 ++++++++ challenge-025/duncan-c-white/perl5/v4.pl | 115 +++++++++ challenge-025/duncan-c-white/perl5/v5.pl | 125 +++++++++ challenge-025/duncan-c-white/perl5/v6.pl | 127 ++++++++++ challenge-025/duncan-c-white/perl5/v7.pl | 178 +++++++++++++ challenge-025/duncan-c-white/perl5/v8.pl | 191 ++++++++++++++ challenge-025/duncan-c-white/perl5/v9.pl | 186 ++++++++++++++ challenge-026/duncan-c-white/README | 57 ++--- challenge-026/duncan-c-white/perl5/ch-1.pl | 38 +++ challenge-026/duncan-c-white/perl5/ch-2.pl | 38 +++ 23 files changed, 3209 insertions(+), 65 deletions(-) create mode 100644 challenge-025/duncan-c-white/blog.txt create mode 100755 challenge-025/duncan-c-white/perl5/ch-1.pl create mode 100755 challenge-025/duncan-c-white/perl5/v0.pl create mode 100755 challenge-025/duncan-c-white/perl5/v1.pl create mode 100755 challenge-025/duncan-c-white/perl5/v15.pl create mode 100755 challenge-025/duncan-c-white/perl5/v16-with-histo.pl create mode 100755 challenge-025/duncan-c-white/perl5/v16.pl create mode 100755 challenge-025/duncan-c-white/perl5/v17.pl create mode 100755 challenge-025/duncan-c-white/perl5/v18.pl create mode 100755 challenge-025/duncan-c-white/perl5/v19.pl create mode 100755 challenge-025/duncan-c-white/perl5/v2.pl create mode 100755 challenge-025/duncan-c-white/perl5/v20.pl create mode 100755 challenge-025/duncan-c-white/perl5/v3.pl create mode 100755 challenge-025/duncan-c-white/perl5/v4.pl create mode 100755 challenge-025/duncan-c-white/perl5/v5.pl create mode 100755 challenge-025/duncan-c-white/perl5/v6.pl create mode 100755 challenge-025/duncan-c-white/perl5/v7.pl create mode 100755 challenge-025/duncan-c-white/perl5/v8.pl create mode 100755 challenge-025/duncan-c-white/perl5/v9.pl create mode 100755 challenge-026/duncan-c-white/perl5/ch-1.pl create mode 100755 challenge-026/duncan-c-white/perl5/ch-2.pl diff --git a/challenge-025/duncan-c-white/README b/challenge-025/duncan-c-white/README index 4cd2bbfe01..49002c5fc3 100644 --- a/challenge-025/duncan-c-white/README +++ b/challenge-025/duncan-c-white/README @@ -1,36 +1,30 @@ -Challenge 1: "Create a script that prints nth order forward difference -series. You should be a able to pass the list of numbers and order number -as command line parameters. Let me show you with an example: +Challenge 1: "Generate a longest sequence of the following "English Pokemon" + names where each name starts with the last letter of the previous name: -Suppose we have list (X) of numbers: 5, 9, 2, 8, 1, 6 and we would like -to create 1st order forward difference series (Y). So using the formula -Y(i) = X(i+1) - X(i), we get the following numbers: (9-5), (2-9), (8-2), -(1-8), (6-1), ie 4, -7, 6, -7, 5. -If you noticed, it has one less number than the original series. -Similarly you can generate the 2nd order forward difference series like: -(-7-4), (6+7), (-7-6), (5+7) => -11, 13, -13, 12. + audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask" -My notes: Clearly defined, very easy - let's have a go.. +My notes: Clearly defined, nice, potentially tricky, let's have a go - + and try some optimization experiments. -Challenge 2: "Create a script that prints Prime Decomposition of a -given number. The prime decomposition of a number is defined as a list -of prime numbers which when all multiplied together, are equal to that -number. For example, the Prime decomposition of 228 is 2,2,3,19 as 228 = -2 * 2 * 3 * 19." +Challenge 2: "Create script to implement Chaocipher. Please checkout +https://en.wikipedia.org/wiki/Chaocipher for more information." -My notes: So, prime factors then. Very easy again. In fact, haven't I -already solved this in one of the other prime-based questions? +My notes: That wikipedia page is very light on details, but refers you +to the following PDF document for a full explanation: +http://www.chaocipher.com/ActualChaocipher/Chaocipher-Revealed-Algorithm.pdf -Challenge 3: "Write a script to use Random Poems API: -https://www.poemist.com/api/v1/randompoems -This is the easiset API, I have come across so far. You don't need API -key for this. They have only route to work with (GET). The API task is -optional but we would love to see your solution." - -My notes: ok, even I can't argue that obtaining an API key for an API -I will literally never use again is too much hassle - when I don't need -an API key, and the whole program appears to be an LWP::Simple get.. - -update: well, apart from the Unicode in the response, complicating life. +reading that, it gives a clear description of the algorithm. but then +shows you the canonical solution - in beautifully clean Perl 5. So, umm, +what is the point in me doing it again? +I already solved this in one of the other prime-based questions? diff --git a/challenge-025/duncan-c-white/blog.txt b/challenge-025/duncan-c-white/blog.txt new file mode 100644 index 0000000000..03df97d466 --- /dev/null +++ b/challenge-025/duncan-c-white/blog.txt @@ -0,0 +1 @@ +https://www.doc.ic.ac.uk/~dcw/PSD/article13/ diff --git a/challenge-025/duncan-c-white/perl5/ch-1.pl b/challenge-025/duncan-c-white/perl5/ch-1.pl new file mode 100755 index 0000000000..a8dc4aa651 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/ch-1.pl @@ -0,0 +1,212 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# +# optimization v20: turned for loop that pushes into push map... +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from letter L to list of word nos of words ENDING with L + +my @inword; # array from word no N to array of wordnos of words going "in" + # to word N, i.e. ending with the first letter of word N + # if there are no such words, then [] + +# build %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + $sw{$firstletter} //= []; + push @{$sw{$firstletter}}, $wn; +} +#die Dumper \%sw; + +# build %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + $ew{$lastletter} //= []; + push @{$ew{$lastletter}}, $wn; +} +#die Dumper \%ew; + +# build @stopword, using %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + my $aref = $sw{$lastletter} // []; + push @stopword, $wn if @$aref==0; +} +#die Dumper [ map { $words[$_] } @stopword ]; + +# build @inword, using %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + my $aref = $ew{$firstletter} // []; + $inword[$wn]= $aref; +} +#die Dumper \@inword; + +# No longer need %sw or %ew.. +undef %sw; +undef %ew; + +my @seqs = findall(); + +show_seqs( @seqs ) if $debug; + +exit 0; + + +# +# my @seqs = findall(); +# Find all sequences, starting with sequences of length 1 (stop words), +# then working back, i.e. prepending words onto the front of existing +# sequences. +# Delivers the list of all maximal-length sequences, each sequence is +# a comma-separated string of word numbers. +# +fun findall() +{ + my $currpaths = []; # list of all paths for sequences of length N + # each path entry is now a triple: + # [ inwordarrayref, seqstr, availarrayref ] + # note that seqstr, representing the sequence, + # is asingle string comprising the + # comma-separated list of word nos + my $N = 1; # length starts at 1 and is increased.. + + # convert each stopword word no into a path triple + @$currpaths = map { [ $inword[$_], $_, availset($_) ] } @stopword; + #die Dumper $currpaths; + + for(;;) + { + #die Dumper $currpaths; + my $nseq = @$currpaths; + print "Have $nseq sequences of length $N\n"; + #show_paths( @$currpaths ); + + # + # Now let's take every path of length N, + # and lengthen them to length n+1, by prepending a + # word number to the start of each sequence. This will + # be possible unless all sequences in currpaths are + # already at their maximal length - when that happens, + # we break out of the loop. + # + my $newpaths = []; # paths of length N+1 + foreach my $path (@$currpaths) # foreach current path + { + my( $inwords, $s, $avail ) = @$path; + + # extend path s by each unused word no in the inwords + push @$newpaths, + map { + # word no $_ no longer available. + my $newavail = $avail; + substr( $newavail, $_, 1 ) = 0; + + # build a whole new path, length N+1 + [ $inword[$_], "$_,$s", $newavail ] + } + grep { substr($avail,$_,1) eq '1' } @$inwords; + } + last if @$newpaths == 0; + $N++; + $currpaths = $newpaths; + #die Dumper $currpaths; + } + + # now extract and return all the maximal length sequences + return map { $_->[1] } @$currpaths; +} + + + +# +# my $set = availset( $wno ); +# Form a set in which all word nos are available, except $wno. +# +fun availset( $wno ) +{ + my $set = 1 x scalar(@words); + substr( $set, $wno, 1 ) = 0; + return $set; +} + + +# +# show_paths( @paths ); +# Show the sequences (as words, not word nos) contained in @paths +# +fun show_paths( @paths ) +{ + foreach my $p (@paths) + { + my $str = join( ',', map { $words[$_] } split(/,/,$p->[1]) ); + say $str; + } +} + + +# +# show_seqs( @seqs ); +# Show the sequence of word numbers (as words, not word nos) +# +fun show_seqs( @seqs ) +{ + foreach my $s (@seqs) + { + my $str = join( ',', map { $words[$_] } split(/,/,$s) ); + say $str; + } +} diff --git a/challenge-025/duncan-c-white/perl5/v0.pl b/challenge-025/duncan-c-white/perl5/v0.pl new file mode 100755 index 0000000000..8172dd4212 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v0.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# +# optimization v1: baseline code before starting to optimize: 32.6s. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +#use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +say "Pokemon names starting with c:"; +my @startwords = grep { /^c/ } @words; +say for @startwords; diff --git a/challenge-025/duncan-c-white/perl5/v1.pl b/challenge-025/duncan-c-white/perl5/v1.pl new file mode 100755 index 0000000000..b48b6a28b5 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v1.pl @@ -0,0 +1,102 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# +# optimization v1: baseline code before starting to optimize: 32.6s. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +my %sw; # hash from letter to list of words starting with that letter. + +foreach my $word (@words) +{ + $word =~ /^(.)/; + my $letter = $1; + $sw{$letter} //= []; + push @{$sw{$letter}}, $word; +} + +#die Dumper \%sw; + +my @longseq = (); # longest sequence found so far.. + +# search for sequences starting with each word in turn.. +foreach my $sw (@words) +{ + findseq( $sw, () ); +} + +my $longest = @longseq; + +print "\nlongest sequence is length $longest: @longseq\n"; +exit 0; + + +# +# findseq( $currw, @seq ); +# Find all sequences of words from $currw onwards, +# given that we've already visited words in @seq, +# and update the global @longseq if any sequences +# we find are longer than that. +# +fun findseq( $currw, @seq ) +{ + push @seq, $currw; # extend @seq sequence + + my %used = map { $_ => 1 } @seq; # convert to set + + $currw =~ /(.)$/; # find the last letter of currw + my $lastletter = $1; + + my $nextw = $sw{$lastletter}; # all words starting with lastletter + if( defined $nextw ) # if there are any, try each word + { + foreach my $nextword (@$nextw) + { + findseq( $nextword, @seq ) + unless $used{$nextword}; + } + } else # @seq is finished + { + #print "found sequence @seq\n"; + my $len = @seq; + if( $len > @longseq ) + { + print "longest seq so far (len $len): @seq\n" if $debug; + @longseq = @seq; + } + } +} diff --git a/challenge-025/duncan-c-white/perl5/v15.pl b/challenge-025/duncan-c-white/perl5/v15.pl new file mode 100755 index 0000000000..156466391f --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v15.pl @@ -0,0 +1,224 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @outword; # array from word no N to array of wordnos of words going "out" + # from word N, i.e. starting with the last letter of word N + # if there are no such words, then [] + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from letter L to list of word nos of words ENDING with L + +my @inword; # array from word no N to array of wordnos of words going "in" + # to word N, i.e. ending with the first letter of word N + # if there are no such words, then [] + +# build %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + $sw{$firstletter} //= []; + push @{$sw{$firstletter}}, $wn; +} +#die Dumper \%sw; + +# build %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + $ew{$lastletter} //= []; + push @{$ew{$lastletter}}, $wn; +} +#die Dumper \%ew; + +# build @outword and @stopword, using %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + my $aref = $sw{$lastletter} // []; + $outword[$wn]= $aref; + push @stopword, $wn if @$aref==0; +} +#die Dumper \@outword; +#die Dumper [ map { $words[$_] } @stopword ]; + +# build @inword, using %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + my $aref = $ew{$firstletter} // []; + $inword[$wn]= $aref; +} +#die Dumper \@inword; + +# No longer need %sw or %ew.. + +my @seqs = findall(); + +show_seqs( @seqs ) if $debug; + +exit 0; + + +# +# my @seqs = findall(); +# Find all sequences, starting with sequences of length 1 (stop words), +# then working back, i.e. prepending words onto the front of existing +# sequences. Delivers the list of all maximal-length sequences. +# +fun findall( ) +{ + my @sus; # array of two SU lists, sus[curr] stores the current + # list of all SUs for sequences of length N, + # sus[1-curr] builds the NEW list of + # all SUs for sequences of length N+1 + # each SU entry is a [ seqarrayref, usedarrayref ] pair + my $N = 1; # length starts at 1 and is increased.. + my $curr = 0; # start using sus[0] for curr, sus[1] for new.. + + # convert each stopword word no into a SU pair + @sus = ( [], [] ); + @{$sus[0]} = map { [ [ $_ ], [ suset($_) ] ] } @stopword; + + for(;;) + { + my $currsus = $sus[$curr]; + #die Dumper $currsus; + my $nseq = @$currsus; + print "Have $nseq sequences of length $N\n"; + #show_sus( @$currsus ); + + # + # Now let's take every SU (sequence and used set) in + # sus[curr], and lengthen them (storing the results + # in sus[1-curr]), prepending a word number to the start + # of each sequence. This will be possible unless all + # sequences in sus[curr] are already at their maximal + # length - when that happens, we break out of the loop. + # + + my $newsus = $sus[1-$curr]; + @$newsus = (); + foreach my $su (@$currsus) # foreach current SU + { + my( $s, $used ) = @$su; + my $firstwno = $s->[0]; + + # list of word nos into s[0] + my $list = $inword[$firstwno]; + + foreach my $wno (grep { ! $used->[$_] } @$list) + { + # make length N+1 sequence, cons(wno,oldseq) + my @oneseq = @$s; + unshift @oneseq, $wno; + + # alter the used array, marking $wno used. + $used->[$wno] = 1; + + # it's a whole new SU! + push @$newsus, [ \@oneseq, [ @$used ] ]; + + # alter used back + $used->[$wno] = 0; + } + } + last if @$newsus == 0; + $N++; + $curr = 1-$curr; + } + + # now extract and return all the maximal length sequences + + my $currsus = $sus[$curr]; + return map { $_->[0] } @$currsus; +} + + + +# +# my @suset = suset( $wno ); +# Form a SUset in which all word nos are unused, except $wno. +# +fun suset( $wno ) +{ + my @suset = (0) x scalar(@words); + $suset[$wno] = 1; + return @suset; +} + + +# +# show_sus( @sus ); +# Show the sequences (as words, not word nos) contained in SUlist @sus +# +fun show_sus( @sus ) +{ + foreach my $s (@sus) + { + my $str = join( ',', map { $words[$_->[0]] } @$s ); + print "$str\n"; + } +} + + +# +# show_seqs( @seqs ); +# Show the sequences (as words, not word nos) +# +fun show_seqs( @seqs ) +{ + foreach my $s (@seqs) + { + my $str = join( ',', map { $words[$_] } @$s ); + print "$str\n"; + } +} diff --git a/challenge-025/duncan-c-white/perl5/v16-with-histo.pl b/challenge-025/duncan-c-white/perl5/v16-with-histo.pl new file mode 100755 index 0000000000..0a9650debb --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v16-with-histo.pl @@ -0,0 +1,278 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; +use FindBin qw($Bin); + +use lib "$Bin/../lib"; +use lib "$ENV{HOME}/lib"; +use lib "."; +use Histo; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @outword; # array from word no N to array of wordnos of words going "out" + # from word N, i.e. starting with the last letter of word N + # if there are no such words, then [] + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from letter L to list of word nos of words ENDING with L + +my @inword; # array from word no N to array of wordnos of words going "in" + # to word N, i.e. ending with the first letter of word N + # if there are no such words, then [] + +# build %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + $sw{$firstletter} //= []; + push @{$sw{$firstletter}}, $wn; +} +#die Dumper \%sw; + +# build %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + $ew{$lastletter} //= []; + push @{$ew{$lastletter}}, $wn; +} +#die Dumper \%ew; + +# build @outword and @stopword, using %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + my $aref = $sw{$lastletter} // []; + + # need to exclude $wn from @w if present + my @w = grep { $_ ne $wn } @$aref; + + $outword[$wn]= \@w; + push @stopword, $wn if @w==0; +} +#die Dumper \@outword; +#die Dumper [ map { $words[$_] } @stopword ]; + +# build @inword, using %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + my $aref = $ew{$firstletter} // []; + + # need to exclude $wn from @w if present + my @w = grep { $_ ne $wn } @$aref; + + $inword[$wn]= \@w; +} +#die Dumper \@inword; + +# No longer need %sw or %ew.. + +my @seqs = findall(); + +show_seqs( @seqs ) if $debug; + +exit 0; + + +# +# my @seqs = findall(); +# Find all sequences, starting with sequences of length 1 (stop words), +# then working back, i.e. prepending words onto the front of existing +# sequences. Delivers the list of all maximal-length sequences. +# +fun findall( ) +{ + my @sus; # array of two SU lists, sus[curr] stores the current + # list of all SUs for sequences of length N, + # sus[1-curr] builds the NEW list of + # all SUs for sequences of length N+1 + # each SU entry is now a triple: + # [ firstwno, seqstr, usedarrayref ] + # (where the sequence is now seqstr, a single string + # comprising the comma-separated list of word nos, + # always starting with firstwno) + my $N = 1; # length starts at 1 and is increased.. + my $curr = 0; # start using sus[0] for curr, sus[1] for new.. + + # convert each stopword word no into a SU triple + @sus = ( [], [] ); + @{$sus[0]} = map { [ $_, $_, [ suset($_) ] ] } @stopword; + + my $maxnused = 0; + my $total = 0; + my $outer = 0; + my $inner = 0; + my %freq; + + my $histo = Histo->new( BINWIDTH => 1 ); + my $histo2 = Histo->new( BINWIDTH => 1 ); + + for(;;) + { + my $currsus = $sus[$curr]; + #die Dumper $currsus; + my $nseq = @$currsus; + print "Have $nseq sequences of length $N\n"; + #show_sus( @$currsus ); + + # + # Now let's take every SU (firstwno/sequence/used set) in + # sus[curr], and lengthen them (storing the results + # in sus[1-curr]), prepending a word number to the start + # of each sequence. This will be possible unless all + # sequences in sus[curr] are already at their maximal + # length - when that happens, we break out of the loop. + # + my $newsus = $sus[1-$curr]; + @$newsus = (); + foreach my $su (@$currsus) # foreach current SU + { + my( $firstwno, $s, $used ) = @$su; + + # find the list of word nos into firstwno + my $list = $inword[$firstwno]; + +$total += @$list; +$outer++; + +my $nused = my @u = grep { $used->[$_] } @$list; +$maxnused = $nused if $nused > $maxnused; +#warn "found $nused used (@u), list=@$list, with firstwno=$firstwno, s=$s!!\n" if $nused > 0; +#warn "all $nused used (@u), list=@$list, with firstwno=$firstwno, s=$s!!\n" if $nused>0 && $nused == @$list; + + # for each unused word no going into firstwno + $histo2->add( scalar(@$list) ); + my @x = grep { ! $used->[$_] } @$list; + # want histogram scalar(@x) + $histo->add( scalar(@x) ); + $freq{ scalar(@x) }++; + + foreach my $wno (@x) + { + $inner++; + + # make length N+1 sequence, cons(wno,oldseq) + my $news = "$wno,$s"; + + # alter the used array, marking $wno used. + $used->[$wno] = 1; + + # build a whole new SU! + push @$newsus, [ $wno, $news, [ @$used ] ]; + + # alter used back + $used->[$wno] = 0; + } + } + last if @$newsus == 0; + $N++; + $curr = 1-$curr; + } + + print "all:\n$histo2\navail:\n$histo\n"; + + printf "maximum used = $maxnused, average num words = %.1f, outer=$outer, inner=$inner\n", + $total/$outer; + + my $t1 = 0; my $t2 = 0; + while( my($k,$v) = each %freq ) + { + $t1 += $k*$v; + $t2 += $v; + } + print "t1=$t1, t2=$t2\n"; + + # now extract and return all the maximal length sequences + + my $currsus = $sus[$curr]; + return map { $_->[1] } @$currsus; +} + + + +# +# my @suset = suset( $wno ); +# Form a SUset in which all word nos are unused, except $wno. +# +fun suset( $wno ) +{ + my @suset = (0) x scalar(@words); + $suset[$wno] = 1; + return @suset; +} + + +# +# show_sus( @sus ); +# Show the sequences (as words, not word nos) contained in SUlist @sus +# +fun show_sus( @sus ) +{ + foreach my $s (@sus) + { + my $str = $s->[1]; + print "$str\n"; + } +} + + +# +# show_seqs( @seqs ); +# Show the sequences (as words, not word nos) +# +fun show_seqs( @seqs ) +{ + foreach my $s (@seqs) + { + my $str = join( ',', map { $words[$_] } split(/,/,$s) ); + print "$str\n"; + } +} diff --git a/challenge-025/duncan-c-white/perl5/v16.pl b/challenge-025/duncan-c-white/perl5/v16.pl new file mode 100755 index 0000000000..24bc7e03b5 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v16.pl @@ -0,0 +1,227 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @outword; # array from word no N to array of wordnos of words going "out" + # from word N, i.e. starting with the last letter of word N + # if there are no such words, then [] + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from letter L to list of word nos of words ENDING with L + +my @inword; # array from word no N to array of wordnos of words going "in" + # to word N, i.e. ending with the first letter of word N + # if there are no such words, then [] + +# build %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + $sw{$firstletter} //= []; + push @{$sw{$firstletter}}, $wn; +} +#die Dumper \%sw; + +# build %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + $ew{$lastletter} //= []; + push @{$ew{$lastletter}}, $wn; +} +#die Dumper \%ew; + +# build @outword and @stopword, using %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + my $aref = $sw{$lastletter} // []; + $outword[$wn]= $aref; + push @stopword, $wn if @$aref==0; +} +#die Dumper \@outword; +#die Dumper [ map { $words[$_] } @stopword ]; + +# build @inword, using %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + my $aref = $ew{$firstletter} // []; + $inword[$wn]= $aref; +} +#die Dumper \@inword; + +# No longer need %sw or %ew.. + +my @seqs = findall(); + +show_seqs( @seqs ) if $debug; + +exit 0; + + +# +# my @seqs = findall(); +# Find all sequences, starting with sequences of length 1 (stop words), +# then working back, i.e. prepending words onto the front of existing +# sequences. Delivers the list of all maximal-length sequences. +# +fun findall( ) +{ + my @sus; # array of two SU lists, sus[curr] stores the current + # list of all SUs for sequences of length N, + # sus[1-curr] builds the NEW list of + # all SUs for sequences of length N+1 + # each SU entry is now a triple: + # [ firstwno, seqstr, usedarrayref ] + # (where the sequence is now seqstr, a single string + # comprising the comma-separated list of word nos, + # always starting with firstwno) + my $N = 1; # length starts at 1 and is increased.. + my $curr = 0; # start using sus[0] for curr, sus[1] for new.. + + # convert each stopword word no into a SU triple + @sus = ( [], [] ); + @{$sus[0]} = map { [ $_, $_, [ suset($_) ] ] } @stopword; + + for(;;) + { + my $currsus = $sus[$curr]; + #die Dumper $currsus; + my $nseq = @$currsus; + print "Have $nseq sequences of length $N\n"; + #show_sus( @$currsus ); + + # + # Now let's take every SU (firstwno/sequence/used set) in + # sus[curr], and lengthen them (storing the results + # in sus[1-curr]), prepending a word number to the start + # of each sequence. This will be possible unless all + # sequences in sus[curr] are already at their maximal + # length - when that happens, we break out of the loop. + # + my $newsus = $sus[1-$curr]; + #@$newsus = (); + undef @$newsus; + foreach my $su (@$currsus) # foreach current SU + { + my( $firstwno, $s, $used ) = @$su; + + # find the list of word nos into firstwno + my $list = $inword[$firstwno]; + + # for each unused word no going into firstwno + foreach my $wno (grep { ! $used->[$_] } @$list) + { + # make length N+1 sequence, cons(wno,oldseq) + my $news = "$wno,$s"; + + # alter the used array, marking $wno used. + $used->[$wno] = 1; + + # build a whole new SU! + push @$newsus, [ $wno, $news, [ @$used ] ]; + + # alter used back + $used->[$wno] = 0; + } + } + last if @$newsus == 0; + $N++; + $curr = 1-$curr; + } + + # now extract and return all the maximal length sequences + + my $currsus = $sus[$curr]; + return map { $_->[1] } @$currsus; +} + + + +# +# my @suset = suset( $wno ); +# Form a SUset in which all word nos are unused, except $wno. +# +fun suset( $wno ) +{ + my @suset = (0) x scalar(@words); + $suset[$wno] = 1; + return @suset; +} + + +# +# show_sus( @sus ); +# Show the sequences (as words, not word nos) contained in SUlist @sus +# +fun show_sus( @sus ) +{ + foreach my $s (@sus) + { + my $str = $s->[1]; + print "$str\n"; + } +} + + +# +# show_seqs( @seqs ); +# Show the sequences (as words, not word nos) +# +fun show_seqs( @seqs ) +{ + foreach my $s (@seqs) + { + my $str = join( ',', map { $words[$_] } split(/,/,$s) ); + print "$str\n"; + } +} diff --git a/challenge-025/duncan-c-white/perl5/v17.pl b/challenge-025/duncan-c-white/perl5/v17.pl new file mode 100755 index 0000000000..403ae75946 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v17.pl @@ -0,0 +1,220 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from letter L to list of word nos of words ENDING with L + +my @inword; # array from word no N to array of wordnos of words going "in" + # to word N, i.e. ending with the first letter of word N + # if there are no such words, then [] + +# build %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + $sw{$firstletter} //= []; + push @{$sw{$firstletter}}, $wn; +} +#die Dumper \%sw; + +# build %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + $ew{$lastletter} //= []; + push @{$ew{$lastletter}}, $wn; +} +#die Dumper \%ew; + +# build @stopword, using %sw +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /(.)$/; + my $lastletter = $1; + my $aref = $sw{$lastletter} // []; + push @stopword, $wn if @$aref==0; +} +#die Dumper [ map { $words[$_] } @stopword ]; + +# build @inword, using %ew +foreach my $wn (0..$#words) +{ + my $word = $words[$wn]; + $word =~ /^(.)/; + my $firstletter = $1; + my $aref = $ew{$firstletter} // []; + $inword[$wn]= $aref; +} +#die Dumper \@inword; + +# No longer need %sw or %ew.. +undef %sw; +undef %ew; + +my @seqs = findall(); + +show_seqs( @seqs ) if $debug; + +exit 0; + + +# +# my @seqs = findall(); +# Find all sequences, starting with sequences of length 1 (stop words), +# then working back, i.e. prepending words onto the front of existing +# sequences. +# Delivers the list of all maximal-length sequences, each sequence is +# a comma-separated string of word numbers. +# +fun findall() +{ + my @pathlist; # array of path lists, pathlist[N-1] stores the + # list of all paths for sequences of length N, + # pathlist[N] will store the list of all paths + # of length N+1 etc; + # each path entry is now a triple: + # [ inwordarrayref, seqstr, availarrayref ] + # (where the sequence is now seqstr, a single string + # comprising the comma-separated list of word nos, + # always starting with firstwno) + my $N = 1; # length starts at 1 and is increased.. + + # convert each stopword word no into a path triple + push @pathlist, + [ map { [ $inword[$_], $_, [ availset($_) ] ] } @stopword ]; + #die Dumper \@pathlist; + + for(;;) + { + my $currpaths = $pathlist[$N-1]; + #die Dumper $currpaths; + my $nseq = @$currpaths; + print "Have $nseq sequences of length $N\n"; + #show_paths( @$currpaths ); + + # + # Now let's take every path of length N in pathlist[N-1], + # and lengthen them to length n+1 (storing the results + # in pathlist[N]), by prepending a word number to the + # start of each sequence. This will be possible unless + # all sequences in paths[curr] are already at their maximal + # length - when that happens, we break out of the loop. + # + my $newpaths = []; + foreach my $path (@$currpaths) # foreach current path + { + my( $inwords, $s, $avail ) = @$path; + + # for each unused word no in the inwords + foreach my $wno (grep { $avail->[$_] } @$inwords) + { + # alter the avail array, marking $wno used. + $avail->[$wno] = 0; + + # build a whole new path, with length N+1 + # sequence, viz: cons(wno,oldseq) + push @$newpaths, + [ + $inword[$wno], "$wno,$s", [ @$avail ] + ]; + + # alter avail back + $avail->[$wno] = 1; + } + } + last if @$newpaths == 0; + $N++; + push @pathlist, $newpaths; + #die Dumper \@pathlist; + } + + # now extract and return all the maximal length sequences + my $currpaths = $pathlist[$N-1]; + return map { $_->[1] } @$currpaths; +} + + + +# +# my @set = availset( $wno ); +# Form a set in which all word nos are available, except $wno. +# +fun availset( $wno ) +{ + my @set = (1) x scalar(@words); + $set[$wno] = 0; + return @set; +} + + +# +# show_paths( @paths ); +# Show the sequences (as words, not word nos) contained in @paths +# +fun show_paths( @paths ) +{ + foreach my $p (@paths) + { + print "$p->[1]\n"; + } +} + + +# +# show_seqs( @seqs ); +# Show the sequence of word numbers (as words, not word nos) +# +fun show_seqs( @seqs ) +{ + foreach my $s (@seqs) + { + my $str = join( ',', map { $words[$_] } split(/,/,$s) ); + print "$str\n"; + } +} diff --git a/challenge-025/duncan-c-white/perl5/v18.pl b/challenge-025/duncan-c-white/perl5/v18.pl new file mode 100755 index 0000000000..7be9061205 --- /dev/null +++ b/challenge-025/duncan-c-white/perl5/v18.pl @@ -0,0 +1,219 @@ +#!/usr/bin/perl +# +# Challenge 1: "Generate a longest sequence of the following "English Pokemon" +# names where each name starts with the last letter of the previous name: +# +# audino bagon baltoy banette bidoof braviary bronzor carracosta +# charmeleon cresselia croagunk darmanitan deino emboar emolga +# exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur +# jellicent jumpluff kangaskhan kricketune landorus ledyba loudred +# lumineon lunatone machamp magnezone mamoswine nosepass petilil +# pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz +# registeel relicanth remoraid rufflet sableye scolipede scrafty +# seaking sealeo silcoon simisear snivy snorlax spoink starly +# tirtouga trapinch treecko tyrogue vigoroth vulpix wailord +# wartortle whismur wingull yamask" +# +# My notes: Clearly defined, nice, potentially tricky, let's do it. +# +# optimization v18: replaced @avail array of booleans with $avail, a STRING +# of 0s and 1s; well over TWICE AS FAST.. +# + +use v5.10; # to get "say" +use strict; +use warnings; +use Function::Parameters; +use Data::Dumper; + +my $debug = @ARGV>0; + +my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta + charmeleon cresselia croagunk darmanitan deino emboar emolga + exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur + jellicent jumpluff kangaskhan kricketune landorus ledyba loudred + lumineon lunatone machamp magnezone mamoswine nosepass petilil + pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz + registeel relicanth remoraid rufflet sableye scolipede scrafty + seaking sealeo silcoon simisear snivy snorlax spoink starly + tirtouga trapinch treecko tyrogue vigoroth vulpix wailord + wartortle whismur wingull yamask); +#@words = qw(hello ollie excellent thanks shelter runaround set to); + +#die scalar(@words); + +my %sw; # hash from letter L to list of word nos of words STARTING with L + +my @stopword;# list of stop word nos (word nos of words with no outwords) + +my %ew; # hash from let