perl logo Perl logo (Thanks to Olaf Alders)

The weekly challenge 371 - Task 2: Subset Equilibrium

  1 #!/usr/bin/env perl
  2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-371/#TASK2
  3 #
  4 # Task 2: Subset Equilibrium
  5 # ==========================
  6 #
  7 # You are given an array of numbers.
  8 #
  9 # Write a script to find all proper subsets with more than one element where
 10 # the sum of elements equals the sum of their indices.
 11 #
 12 ## Example 1
 13 ##
 14 ## Input: @nums = (2, 1, 4, 3)
 15 ## Output: (2, 1), (1, 4), (4, 3), (2, 3)
 16 ##
 17 ## Subset 1: (2, 1)
 18 ## Values: 2 + 1 = 3
 19 ## Positions: 1 + 2 = 3
 20 ##
 21 ## Subset 2: (1, 4)
 22 ## Values: 1 + 4 = 5
 23 ## Positions: 2 + 3 = 5
 24 ##
 25 ## Subset 3: (4, 3)
 26 ## Values: 4 + 3 = 7
 27 ## Positions: 3 + 4 = 7
 28 ##
 29 ## Subset 4: (2, 3)
 30 ## Values: 2 + 3 = 5
 31 ## Positions: 1 + 4 = 5
 32 #
 33 ## Example 2
 34 ##
 35 ## Input: @nums = (3, 0, 3, 0)
 36 ## Output: (3, 0), (3, 0, 3)
 37 ##
 38 ## Subset 1: (3, 0)
 39 ## Values: 3 + 0 = 3
 40 ## Positions: 1 + 2 = 3
 41 ##
 42 ## Subset 2: (3, 0, 3)
 43 ## Values: 3 + 0 + 3 = 6
 44 ## Positions: 1 + 2 + 3 = 6
 45 #
 46 ## Example 3
 47 ##
 48 ## Input: @nums = (5, 1, 1, 1)
 49 ## Output: (5, 1, 1)
 50 ##
 51 ## Subset 1: (5, 1, 1)
 52 ## Values: 5 + 1 + 1 = 7
 53 ## Positions: 1 + 2 + 4 = 7
 54 #
 55 ## Example 4
 56 ##
 57 ## Input: @nums = (3, -1, 4, 2)
 58 ## Output: (3, 2), (3, -1, 4)
 59 ##
 60 ## Subset 1: (3, 2)
 61 ## Values: 3 + 2 = 5
 62 ## Positions: 1 + 4 = 5
 63 ##
 64 ## Subset 2: (3, -1, 4)
 65 ## Values: 3 + (-1) + 4 = 6
 66 ## Positions: 1 + 2 + 3 = 6
 67 #
 68 ## Example 5
 69 ##
 70 ## Input: @nums = (10, 20, 30, 40)
 71 ## Output: ()
 72 #
 73 ############################################################
 74 ##
 75 ## discussion
 76 ##
 77 ############################################################
 78 #
 79 # We create all a temporary array with all elements from @nums
 80 # with their index appended after ":". That way, we can just
 81 # create all possible subsets by using Data::PowerSet's
 82 # powerset(). From the powerset, we discard the empty set as
 83 # well as all sets with only one element and the set that
 84 # contains all elements (as it is not a proper subset). We also
 85 # discard all non-matching subsets, with non-matching being the
 86 # ones where the sum of the elements and the sum of the indices
 87 # don't match (we us a little helper function to determine that).
 88 
 89 use v5.36;
 90 use Data::PowerSet qw(powerset);
 91 
 92 subset_equilibrium(2, 1, 4, 3);
 93 subset_equilibrium(3, 0, 3, 0);
 94 subset_equilibrium(5, 1, 1, 1);
 95 subset_equilibrium(3, -1, 4, 2);
 96 subset_equilibrium(10, 20, 30, 40);
 97 
 98 sub subset_equilibrium(@nums) {
 99     say "Input: (" . join(", ", @nums) . ")";
100     my $subsets = generate_subsets(@nums);
101     my @result = ();
102     foreach my $subset (@$subsets) {
103         next unless @$subset;
104         next unless is_matching($subset);
105         next if scalar(@$subset) == scalar(@nums); # all elements != proper subset
106         next if scalar(@$subset) <= 1; # not more than one element: discard
107         my @tmp = ();
108         foreach my $elem (@$subset) {
109             $elem =~ s/:.*//;
110             push @tmp, $elem;
111         }
112         push @result, [ @tmp ];
113     }
114     print "Output: (";
115     foreach my $elem (@result) {
116         print "[" . join(", ", @$elem) . "], "
117     }
118     say ")";
119 }
120 
121 sub is_matching($subset) {
122     my @elems = @$subset;
123     my $sum_elems = 0;
124     my $sum_index = 0;
125     foreach my $elem (@elems) {
126         my ($e, $i) = split /:/, $elem;
127         $sum_elems += $e;
128         $sum_index += $i;
129     }
130     return $sum_elems == $sum_index;
131 }
132 
133 sub generate_subsets(@elems) {
134     my @tmp = ();
135     foreach my $i (1..$#elems+1) {
136         push @tmp, "$elems[$i-1]:$i";
137     }
138     return powerset(@tmp);
139 }