Hi, here is my second solution for this very interesting quiz.

My first solution took an estimated one hour in the worst case (when no 
exact solution exists), this one is A LOT faster. The actual time 
depends a bit on the input values, but I have never seen it take more 
than 8 seconds.

This solution works by combining two sub terms at a time into new 
composite terms until either a term with the target value is found or 
all combinations have been generated.
The most important optimization was to ignore a new term if another term 
of the same value using the same input numbers has already been found. 
To further improve the speed I also decided not to allow terms giving 
fractions, zero or negative values.

So here it is:

class Solver
   class Term
     attr_reader :value, :mask

     def initialize(value, mask, op = nil, left = nil, right = nil)
       @value = value
       @mask = mask
       @op = op
       @left = left
       @right = right
     end

     def to_s
       return @value.to_s unless @op
       "(#@left #@op #@right)"
     end
   end

   def initialize(sources, target)
     printf "%s -> %d\n", sources.inspect, target
     @target = target
     @new_terms = []
     @num_sources = sources.size

     # the hashes are used to check for duplicate terms
     # (terms that have the same value and use the same
     # source numbers)
     @term_hashes = Array.new(1 << @num_sources) { {} }

     # enter the source numbers as (simple) terms
     sources.each_with_index do |value, index|

       # each source number is represented by one bit in the bit mask
       mask = 1 << index
       term = Term.new(value, mask)
       @new_terms << term
       @term_hashes[mask][value] = term
     end
   end

   def run
     best_difference = (@target * 1000).abs
     next_new_terms = [nil]
     until next_new_terms.empty?
       next_new_terms = []

       # temporary hashes for terms found in this iteration
       # (again to check for duplicates)
       new_hashes = Array.new(1 << @num_sources) { {} }

       # iterate through all the new terms (those that weren't yet used
       # to generate composite terms)
       @new_terms.each do |term|

         # iterate through the hashes and find those containing terms
         # that share no source numbers with 'term'
         @term_hashes.each_with_index do |hash, index|
           next if term.mask & index != 0

           # iterate through the hashes and build composite terms using
           # the four basic operators
           hash.each_value do |other|
             [:+, :-, :*, :/].each do |op|

               # don't allow fractions
               # if you want to allow fractions remove this line and
               # make sure that the source numbers are floats (or
               # include rational.rb)
               next if op == :/ && term.value % other.value != 0

               # calculate value of composite term
               value = term.value.send(op, other.value)

               # don't allow negative values or zero
               # (negative subterms are not necessairy as long as the
               # target is positive)
               next if value <= 0

               # ignore this composite term if this value was already
               # found for a different term using the same source
               # numbers
               mask = term.mask | other.mask
               next if @term_hashes[mask].has_key?(value) ||
                 new_hashes[mask].has_key?(value)

               new_term = Term.new(value, mask, op, term, other)

               # if the new term is closer to the target than the
               # best match so far print it out
               if (value - @target).abs < best_difference
                 best_difference = (value - @target).abs
                 printf "%s = %d (error: %d)\n", new_term, value,
                                     best_difference
                 return if best_difference == 0
               end

               # remember the new term for use in the next iteration
               next_new_terms << new_term
               new_hashes[mask][value] = new_term
             end
           end
         end
       end

       # merge the hashes with the new terms into the main hashes
       @term_hashes.each_with_index do |hash, index|
         hash.merge!(new_hashes[index])
       end

       # the newly found terms will be used in the next iteration
       @new_terms = next_new_terms
     end
   end
end

if ARGV[0] && ARGV[0].downcase == 'random'
   ARGV[0] = rand(900) + 100
   ARGV[1] = (rand(4) + 1) * 25
   5.times {|i| ARGV[i + 2] = rand(10) + 1}
end

if ARGV.size < 3
   puts "Usage: ruby countdown.rb <target> <source1> <source2> ..."
   puts "   or: ruby countdown.rb random"
   exit
end

start_time = Time.now
Solver.new(ARGV[1..-1].map {|v| v.to_i}, ARGV[0].to_i).run
printf "%f seconds\n", Time.now - start_time