"Ruby Quiz" <james / grayproductions.net> wrote in message > This week's Ruby Quiz is to write a program that fairly divides treasures, > based on worth. Here's mine. I reused several methods from the wierd numbers quiz. ############################################# #loot.rb #Adam Shelly #evenly splits an array into N parts with equal value , if possible class Array def sum inject(0){|s,v| s+v} end def subtract arr return clear if arr==self arr.each{|e| if (n=index(e)) then delete_at(n); end } self end #fast version which misses some subsets. #useful as a rough filter. def quick_find_subset_with_sum n a = self.sort.reverse sum,set = 0,[] a.each {|e| if (sum+e <= n) sum+=e set<<e return set if sum == n end } nil end def find_subset_with_sum n s = quick_find_subset_with_sum n return s if s possibilities, seen = [self.select{|e| e<=n}],{} until possibilities.empty? candidate = possibilities.pop diff = candidate.sum - n return candidate if diff == 0 break if diff < 0 candidate.each_with_index{|e,i| break if e > diff new_cand = (candidate.dup) new_cand.delete_at(i) return new_cand if e == diff possibilities << new_cand if !seen[new_cand] seen[new_cand]=true } end nil end end # Splitter algorithm #1: put all loot in pile 1 #2: find a share from pile 1 #3: if you can't find one, it can't be split #4: find a share in the remaining pile #5: repeat unitl you find all shares #6: if you can't find enough shares #7: move the first share to pile2 #8: repeat from step 2, but add pile2 to the remainder in step 4 # this serves to shuffle the possible combinations, until you find one that works. def splitter n, loot splits=[] pile1,pile2=loot.dup.sort.reverse,[] total = loot.sum share = total/n return nil if total%n != 0 || loot.size < n || loot.max > share until pile1.empty? splits[0] = pile1.find_subset_with_sum(share) break if !splits[0] remaining = pile1.subtract(splits[0])+pile2 (1...n).each do |i| break if nil == (splits[i] = remaining.find_subset_with_sum(share)) remaining.subtract(splits[i]) end return splits if splits[n-1] pile2 += splits[0] end return nil end if __FILE__ == $0 if ARGV.size < 2 || ARGV[0].to_i < 1 puts "Usage: #{$0} partners item1 item2 ..." else shares = splitter(ARGV.shift.to_i, ARGV.map{|a| a.to_i }) if !shares puts "This loot can not be evenly divided into #{n} parts!" else shares.each_with_index{|share,i| puts "#{i}: #{share.join(' ')}"} puts "everyone gets #{shares[0].sum}" end end end ############################################# -Adam