in reply to Another word puzzle with too many permutations

Here is a backtracking method which works on sorted data, dogs with rare letters first. (Similar to LanX's proposal.)

use strict; use warnings; use Data::Dumper; sub score { my ($dog, $letters) = @_; my $sum = 0; $sum += $$letters{$_}//0 for grep {/\w/} split //, $dog; return $sum/length($dog); } my @n = qw(Affenpinscher AfghanHound AiredaleTerrier Akita AlaskanMala +mute AmericanEnglishCoonhound AmericanEskimoDog AmericanFoxhound Base +nji BassetHound Beagle BeardedCollie Beauceron BlackandTanCoonhound B +loodhound BluetickCoonhound BorderCollie BorderTerrier Borzoi BostonT +errier BouvierdesFlandres Boxer BoykinSpaniel Briard Brittany Brussel +sGriffon BullTerrier Bulldog Bullmastiff ChesapeakeBayRetriever Chihu +ahua ChineseCrested ChineseShar-Pei Chinook ChowChow ClumberSpaniel C +ockerSpaniel Collie Curly-CoatedRetriever Dachshund Dalmatian DandieD +inmontTerrier DobermanPinscher DoguedeBordeaux EnglishCockerSpaniel E +nglishFoxhound EnglishSetter EnglishSpringerSpaniel GermanPinscher Ge +rmanShepherdDog GermanShorthairedPointer GermanWirehairedPointer Gian +tSchnauzer GlenofImaalTerrier GoldenRetriever GordonSetter GreatDane +Greyhound Harrier IbizanHound IcelandicSheepdog IrishSetter IrishTerr +ier Kuvasz LabradorRetriever LakelandTerrier LhasaApso Lowchen Maltes +e ManchesterTerrier Mastiff MiniatureBullTerrier MiniaturePinscher Mi +niatureSchnauzer NeapolitanMastiff Newfoundland NorfolkTerrier Peking +ese PembrokeWelshCorgi PharaohHound Plott PortugueseWaterDog Pug Puli + PyreneanShepherd RatTerrier ShibaInu ShihTzu SiberianHusky SilkyTerr +ier SkyeTerrier SmoothFoxTerrier SoftCoatedWheatenTerrier SpinoneItal +iano St.Bernard StaffordshireBullTerrier WireFoxTerrier WirehairedPoi +ntingGriffon Xoloitzcuintli YorkshireTerrier); my $letters = "A(8),B(2),C(5),D(8),E(7),F(1),G(3),H(9),I(8),K(1),L(9), +M(1),N(12),O(13),P(1),Q(0),R(5),S(6),T(4),U(6),V(0),W(1),X(1),Y(0),Z( +1)"; my %letters = map { /(\w)\((\d+)\)/; { $1 => $2 } } split /,/, $letter +s; @n = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, score( $ +_, \%letters ) ] } @n; $letters = join '', sort { length $a <=> length $b } map { /(\w)\((\d+ +)\)/; $2?$1 x $2:'' } split /,/, $letters; sub dogtree { my ( $level, $tree, $letters, @dogs ) = @_; print "$tree\n" and exit unless $letters; DOG: while( my $dog = shift @dogs) { my $remaining = $letters; $remaining =~ s/$_//i or next DOG for grep { /\w/ } split //, $dog +; dogtree( $level+1, "$tree $dog", $remaining, @dogs ); } } dogtree 0, '', $letters, @n;

Takes 14 seconds on my machine to find one solution.

Replies are listed 'Best First'.
Re^2: Another word puzzle with too many permutations
by sarchasm (Acolyte) on Oct 15, 2013 at 21:34 UTC
    That works very well and it's pretty quick too. I just started running it with a much bigger set of data so I will see what happens. Thank you!
        > This looks like a multiple-constrained Knapsack problem.

        Yes, kind off.

        > All bets are off...

        Well IMHO the multidimensionality makes it much easier to solve.

        The trick is always to always concentrate on the smallest dimension.

        E.g. there is no V allowed, about 6 dog-names have a V, so they can be excluded right away for the rest of the search.

        Then there is only 1 Z allowed, only about 7 dog-names include a Z.

        After trying each Z-names out in the first level, all other Z-Names must be excluded for the next levels.

        And trying one Z-name also diminishes other characters which become minimal now, so other names can be excluded for the subtree. (e.g. Xoloitzcuintli includes an X, but only one X was allowed, all other X-names must be excluded now, and so on)

        IMHO the search tree becomes comparatively small with this strategy. (brute force has a worst case of faculty(n) combinations to check with n=100 here)

        Cheers Rolf

        ( addicted to the Perl Programming Language)

      A bit more experimentation with sorting and scoring functions has (sadly) lead me to the conclusion that the fast performance of my posted code is more of an accident rather than due to a great algorithm, i.e. it works very well for this data set but not necessarily on others. So be careful before you bet too much on it...

        I noticed it too, that you score caps only. That said, if you score by length, it works nicely as well.
        Another smart thing is to remove impossible words at earliest opportunity.
        DOG: for my $dog (@_) { my $remaining = $letters; $remaining =~ s/$_//i or next DOG for split //, $dog; push @remain, $remaining; push @dogs, $dog; } while( my $dog = shift @dogs ) { dogtree( "$tree $dog", shift @remain, @dogs ); }
        The given puzzle solves in 1-2 secs, and takes about 3 minutes for a full pass (return instead of exit).
        Only one solution exists. A letter set such as
        "A(11),B(3),C(2),D(8),E(6),F(1),H(8),I(10),J(1),K(1),L(6),M(1),N(10),O(9),P(2),R(7),S(6),T(9),U(7),V(1),W(1),X(1),Z(2)"; takes some 20 times longer...