Thanks.  Helped me track down a fix... I think. :)  How did you guys  
came up with all these difficult tests?

Thanks,
Bill

On Feb 8, 2006, at 2:19 PM, Simon Kr÷šer wrote:

> Bill Dolinar wrote:
>> So it doesn't.  But it says it won't split quickly! :)  Do you  
>> have a  solutions for these and list of other tests I could throw  
>> at it?
>>> 61, 63, 21, 87, 64, 84, 96, 35, 14, 74, 20, 62, 36, 64, 6, 14,  
>>> 54,  53, 46, 84, 62, 10, 64, 33, 32, 24, 89
>>>
>>> 8-ways
>
> 64 64 35 6
> 96 63 10
> 89 32 20 14 14
> 84 64 21
> 84 61 24
> 74 62 33
> 87 46 36
> 62 54 53
>
>>> 7, 21, 30, 54, 52, 99, 77, 85, 56, 28, 80, 17, 60, 60, 38, 68,  
>>> 53,  80, 75, 85, 9
>>>
>>> 7-ways
>
> 80 75 7
> 99 54 9
> 85 60 17
> 60 53 28 21
> 80 52 30
> 68 56 38
> 85 77
>
> If my solution is correct :)
>
> cheers
>
> Simon
>

=begin
Here's my 2nd try.  My last try missed one combination when
backtracking.  This time I tested it by writing code to go through
all the possible combinations adding up to a given sum and number
of partners, but it seems like it's the larger summed tests that
find the bugs.  I also changed the part that calculates the
minimum number of elements in a combination to check, which made
it faster than before.  It's still lags a bit behind the speed of
Manuel Kasten's solution.
=end

def split_equally(partners, a)
   a = a.sort
   total = a.inject {|sum, n| sum + n}

   # check for sum not multiple of partners
   return nil if total % partners != 0

   split = total/partners

   # check for largest greater than split
   return nil if a.last > split

   if a.last == split
     # solution with last item
     return split_subset(partners, [a.last], a[0...(a.size-1)])
   else

     sum = 0
     min_elements = 2
     (a.size-1).downto(0) do |i|
       sum += a[i]
       if sum >= split
         min_elements = [a.size - i, 2].max
         break
       end
     end

     max_elements = a.size/partners

     # search for solution with combinations of valid # elements
     (min_elements..max_elements).each do |items|
       combo = Combinations.new(a, a.size - items)
       begin
         more_combos, solution, leftover = find_one_split(split,  
combo, a)
         if (solution != nil && solution.compact != [])
           solution = split_subset(partners, solution, leftover)
         end
         if solution != nil && solution.compact != []
           return solution
         end
       end while more_combos
     end
   end
   nil
end


# return solution for two partners or recurse back
# to split_equally for more
def split_subset(partners, solution, leftover)
   if partners == 2
     return [leftover] + [solution]
   else
     leftover_solution = split_equally(partners - 1, leftover)
     if leftover_solution != nil
       return leftover_solution << solution
     end
   end
   nil
end


# look for one split by beginning with passed combination (missing
# one item) and then working down
def find_one_split(sum, combo, a)
   finished = false
   until finished
     first_item = combo.index_of(1)
     if first_item > 0
       leftover = sum - combo.sum
       if leftover > a[first_item-1]
         combo.skip_smallest
       elsif a.first <= leftover
         matching = a[0..first_item].index(leftover)
         if matching != nil
           solution, leftover = combo.split(a, matching)
           more_combos = combo.next_smaller
           return more_combos, solution, leftover
         end
       end
     end
     finished = !combo.next_smaller
   end
   return false
end


class Combinations
   attr_accessor :bits
   attr_accessor :sum
   def initialize(a, max_zero)
     @bits = Array.new(a.size) {|i| i > max_zero ? 1 : 0}
     @a = a
     @sum = find_sum
   end

   def [](i)
     @bits[i]
   end

   def []=(i, value)
     if @bits[i] == 1 && value == 0
       @sum -= @a[i]
     elsif @bits[i] == 0 && value == 1
       @sum += @a[i]
     end
     @bits[i] = value
   end

   def index_of(value)
     i = @bits.index(value)
   end

   # next smaller combination with same number of items
   def next_smaller
     first_item = @bits.index(1)
     if first_item == 0
       skipped = 0
       @bits.each_with_index do |n, i|
         if n == 1
           self[i] = 0
           if i <= skipped
             skipped += 1
           else
             self[i] = 0
             (i-1).downto(i-1-skipped) {|i| self[i] = 1}
             return true
           end
         end
       end
     else
       self[first_item - 1] = 1
       self[first_item] = 0
       return true
     end
     return false
   end

   def skip_smallest
     first_item = @bits.index(1)
     self[first_item] = 0
     self[0] = 1
   end

   def split(a, index)
     i = -1
     a.partition {|n| i += 1; @bits[i] == 1 || i == index;}
   end
private
   def find_sum
     total = 0
     @a.each_with_index {|n, i| total += n if @bits[i] != 0}
     total
   end
end

if __FILE__ == $0
   a = ARGV.collect {|n| n.to_i}
   partners = a.shift
   split = split_equally(partners, a)
   if split == nil
     print "It is not possible to fairly split this treasure "
     print "#{partners} ways.\n"
   else
     split.each_with_index do |n,i|
       print "#{i}: ", n.join(" "), "\n"
     end
   end
end