[QUIZ] Getting to 100 (#119)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Gavin Kistner

The NPR show "Car Talk" has regular quizzes that they call "Puzzlers"[1]. The
one listed on their web site for March 12th[2] is titled "Getting to 100". In
the quiz, you are supposed to write down the digits 1-9 in order, followed by "
= 100", and then insert between them two minus symbols and one plus symbol (in
any order) to make the formula correct. You aren't allowed to re-arrange digits,
or do some toothpick math like combine two minus signs to make a plus. You must
use every digit, and all three operators. For example, here's one incorrect
solution:

  123 + 45 - 67 - 89 = 100 (This is an incorrect formula; it totals 12)

The quiz, then, is to solve this problem without thinking, instead letting the
computer think for you. Your program should output every possible equation that
can be formed, and the actual result of that equation. The equation that results
in 100 should have stars around it. At the end, you should print out the number
of formulae that were possible. Here's an excerpt of some example output:

  ...
  12 - 34 - 567 + 89 = -500
  12 - 34 + 567 - 89 = 456
  12 + 34 - 567 - 89 = -610
  ************************
  123 - 45 - 67 + 89 = 100
  ************************
  123456 - 7 - 8 + 9 = 123450
  123456 - 7 + 8 - 9 = 123448
  123456 + 7 - 8 - 9 = 123446
  ...
  168 possible equations tested

You should not print the same equation more than once. ("1 - 2 - 3 + 456789" is
the same as "1 - 2 - 3 + 456789", even if the computer thinks that the two minus
symbols come in a different order.)

Extra Credit: Write your program to accept an arbitrary number and ordering of
digits, an arbitrary set of operators (but allowing the same operator more than
once), and an arbitrary target number that the equation is supposed to evaluate
to.

  [1] 2007 Puzzler Index: http://www.cartalk.com/content/puzzler/2007.html
  [2] http://www.cartalk.com/content/puzzler/transcripts/200711/index.html

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Gavin Kistner

The NPR show "Car Talk" has regular quizzes that they call "Puzzlers"[1]. The
one listed on their web site for March 12th[2] is titled "Getting to 100". In
the quiz, you are supposed to write down the digits 1-9 in order, followed by "
= 100", and then insert between them two minus symbols and one plus symbol (in
any order) to make the formula correct. You aren't allowed to re-arrange digits,
or do some toothpick math like combine two minus signs to make a plus. You must
use every digit, and all three operators. For example, here's one incorrect
solution:

        123 + 45 - 67 - 89 = 100 (This is an incorrect formula; it totals 12)

The quiz, then, is to solve this problem without thinking, instead letting the
computer think for you. Your program should output every possible equation that
can be formed, and the actual result of that equation. The equation that results
in 100 should have stars around it. At the end, you should print out the number
of formulae that were possible. Here's an excerpt of some example output:

        ...
        12 - 34 - 567 + 89 = -500
        12 - 34 + 567 - 89 = 456
        12 + 34 - 567 - 89 = -610
        ************************
        123 - 45 - 67 + 89 = 100
        ************************
        123456 - 7 - 8 + 9 = 123450
        123456 - 7 + 8 - 9 = 123448
        123456 + 7 - 8 - 9 = 123446
        ...
        168 possible equations tested

You should not print the same equation more than once. ("1 - 2 - 3 + 456789" is
the same as "1 - 2 - 3 + 456789", even if the computer thinks that the two minus
symbols come in a different order.)

Extra Credit: Write your program to accept an arbitrary number and ordering of
digits, an arbitrary set of operators (but allowing the same operator more than
once)

sorry for being picky but I guess it might be useful
- an arbitrary set of operators
+ an array of operators that can come in any order
hopefully somebody will find the correct formal expression to describe
the beast ...
, and an arbitrary target number that the equation is supposed to evaluate

to.

        [1] 2007 Puzzler Index: http://www.cartalk.com/content/puzzler/2007.html
        [2] http://www.cartalk.com/content/puzzler/transcripts/200711/index.html

For the rest sounds like fun.

Cheers
Robert

···

On 4/6/07, Ruby Quiz <james@grayproductions.net> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

[Ruby Quiz <james@grayproductions.net>, 2007-04-06 14.55 CEST]
[...]

The NPR show "Car Talk" has regular quizzes that they call "Puzzlers"[1]. The
one listed on their web site for March 12th[2] is titled "Getting to 100". In
the quiz, you are supposed to write down the digits 1-9 in order, followed by "
= 100", and then insert between them two minus symbols and one plus symbol (in
any order) to make the formula correct. You aren't allowed to re-arrange digits,
or do some toothpick math like combine two minus signs to make a plus. You must
use every digit, and all three operators.

Can the plus and minus symbols be used as unary operators?

···

--

James Gray wrote:

The three rules of Ruby Quiz:

Yay. I remember doing this exercise in math class back there in
secondary school. Not to be conceited, but I remember having so much fun
with that I tried some variations on the exercise, finding around 200
solutions or so. But, of course, I', crazy :smiley:

I want to suggest some extra credits for all those crazy people put
there ;D

- You can use parenthesis to change precedence of operators (if you use
ore than addition and subtraction)
- You can use decimal dot in numbers. As for example: .1 - 2 + (3 * 4) +
5 + 6 + 78.9 = 100
- Given the set of numbers, rules and operations, say if it can yield
all numbers on a given range, or try to find the largest range possible.

Certainly I love these quizes. =D

···

--
Posted via http://www.ruby-forum.com/\.

Here's my solution. It handles both extra credit suggestions, and
adds control over the verbosity of the output, and some options for
finding 'interesting' inputs.

I found this quiz rather easy. I had a basic solution in about 15
minutes, and spent about another half hour or so generalizing it.

The main problem was partitioning the string of digits in order to
place the operators. I did this with a recursive method on String
which iterates over the possible partitionings using block
composition.

In order to generalize the inputs I used Florian Frank's permutation
gem to get the permutations of the operators.

require 'rubygems'
require 'permutation'

class String

  # yield each partitioning of the receiver into count partitions

···

#
  # This is a recursive implementation using composition of the block argument.
  #
  # The approach is to iteratively split the string into two parts,
  # an initial substring of increasing length, and the remainder.
  # For each initial substring we yield an array which is the concatenation
  # of the initial substring and the partitioning of the remainder of
the string # into count-1 partitions.
  def each_partition(count, &b)
    # if the count is 1 then the only partition is the entire string.
    if count == 1
      yield [self]
    else
      # Iterate over the initial substrings, the longest initial
substring must leave
      # at least count-1 characters in the remaining string.
      (1..(size-(count-1))).each do |initial_size|
        self[initial_size..size].each_partition(count-1) {|remaining|
          b.call([self[0,initial_size]] + remaining)}
      end
    end
  end
end

# print combinations of digits and operators which evaluate to a goal
#
# Arguments are supplied by a hash the keys are:
#
# Main arguments
# :goal - the number being sought, default is 100
# :digits - a string of digits, default is "123456789"
# :ops - an array of strings representing the operators to be inserted into
# digits, default is %w[- - +]
#
# Additional arguments
# :verbose - unless false, print all attempts, default is false
# :return_counts - unless false, return an array of value, count arrays for
# values with multiple solutions, used to find interesting
# inputs, default is false
def get_to(options={})
  options = {
    :goal => 100,
    :digits => '123456789',
    :ops => %w[- - +],
    :verbose => false,
    :return_counts => false
  } .merge(options)
  digits= options[:digits]
  goal, digits, ops, verbose, return_counts =
*options.values_at(:goal, :digits, :ops, :verbose, :return_counts)
  operators = Permutation.for(ops).map{|perm| perm.project}.uniq
  puts "Looking for #{goal}, digits=#{digits}, operators=#{ops.inspect}"
  counts = Hash.new(0)
  found_in_a_row = 0
  digits.each_partition(ops.size + 1) do |numbers|
    operators.each do |ops|
      op_index = -1
      eqn = numbers.zip(ops).flatten.compact.join(' ')
      val = eval(eqn)
      counts[val] += 1 if return_counts
      found = val == goal
      puts "********************************" if found_in_a_row == 0 && found
      puts "********************************" unless found_in_a_row ==
0 || found
      puts "#{eqn} = #{val}" if verbose || goal == val
      found_in_a_row = found ? found_in_a_row + 1 : 0
    end
  end
  return_counts ? counts.select {|key,value| value > 1} : nil
end

get_to
get_to(:verbose=> true)
get_to(:goal => 357, :ops => %w[+ - +], :verbose=> true)
get_to(:goal => 302, :digits => '987654321')
get_to(:goal => 98, :verbose => true)
p get_to(:goal => 336)
get_to(:goal => -355)

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

My solution below uses simple recursion - hopefully it's easy to read
and understand. It meets the extra credit criteria.

Looking forward to seeing how some of the power of Ruby could be used
by others to create a more elegant solution. (I admit my solution is
a bit lacklustre!)

Thanks to Gavin & James.

# Marcel Ward <wardies ^a-t^ gmaildotcom>
# Saturday, 2006-04-07
# Solution for Ruby Quiz #119 <http://www.rubyquiz.com/>

···

#
################################################
# getting-to-x.rb

class GettingToX
  def initialize(no_of_plusses, no_of_minusses, target_number)
    @plusses = no_of_plusses
    @minusses = no_of_minusses
    @target = target_number
  end

  # Recursively called whilst preparing the calculation string,
  # which is passed in calc_prefix
  def prepare_sum(rem_plus, rem_minus, cur_digit, calc_prefix)
    cur_digit += 1

    # Do we have any remaining plus signs to use up?
    if rem_plus > 0
      prepare_sum(rem_plus - 1, rem_minus, cur_digit,
        calc_prefix + " + %c" % (?0 + cur_digit))
    end

    # Do we have any remaining minus signs to use up?
    if rem_minus > 0
      prepare_sum(rem_plus, rem_minus - 1, cur_digit,
        "#{calc_prefix} - %c" % (?0 + cur_digit))
    end

    digits_remaining = 10 - cur_digit
    if rem_plus + rem_minus < digits_remaining
      # We can use a digit here and we'll still have room to
      # fit in all our operators later
      prepare_sum(rem_plus, rem_minus, cur_digit,
        "#{calc_prefix}%c" % (?0 + cur_digit))
    elsif rem_plus + rem_minus == 0
      # We have run out of operators; use up all the digits
      cur_digit.upto(9) {|n| calc_prefix += "%c" % (?0 + n)}
      calc(calc_prefix)
    end
  end

  # Print out the sum (with highlights if the target value was hit).
  def calc(whole_sum)
    result = eval(whole_sum)
    target_hit = (result == @target)
    puts '*' * 30 if target_hit
    puts whole_sum + ' = ' + result.to_s
    puts '*' * 30 if target_hit
    @total_evals += 1
    @target_matches += 1 if target_hit
  end

  def do_sums
    @total_evals = 0
    @target_matches = 0
    # We must always start with a string containing the first digit,
    # i.e. "1" because after this comes either the next digit or +/-
    prepare_sum(@plusses, @minusses, 1, '1')
    puts "#{@total_evals} possible equations tested"
    @target_matches
  end
end

# Show results for 1 plus, 2 minusses, target of 100
x = GettingToX.new(1, 2, 100)
matches = x.do_sums

# How did we do?
puts "** #{matches} equation(s) matched target value **"

--
Marcel

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until 48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone on Ruby Talk follow the discussion. Please reply to the
original quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

···

On Fri, 06 Apr 2007 21:55:37 +0900, Ruby Quiz wrote:
=-=-=-=-=

by Gavin Kistner

The NPR show "Car Talk" has regular quizzes that they call
"Puzzlers"[1]. The one listed on their web site for March 12th[2] is
titled "Getting to 100". In the quiz, you are supposed to write down the
digits 1-9 in order, followed by " = 100", and then insert between them
two minus symbols and one plus symbol (in any order) to make the formula
correct. You aren't allowed to re-arrange digits, or do some toothpick
math like combine two minus signs to make a plus. You must use every
digit, and all three operators. For example, here's one incorrect
solution:

  123 + 45 - 67 - 89 = 100 (This is an incorrect formula; it

totals 12)

The quiz, then, is to solve this problem without thinking, instead
letting the computer think for you. Your program should output every
possible equation that can be formed, and the actual result of that
equation. The equation that results in 100 should have stars around it.
At the end, you should print out the number of formulae that were
possible. Here's an excerpt of some example output:

  ...
  12 - 34 - 567 + 89 = -500
  12 - 34 + 567 - 89 = 456
  12 + 34 - 567 - 89 = -610
  ************************
  123 - 45 - 67 + 89 = 100
  ************************
  123456 - 7 - 8 + 9 = 123450
  123456 - 7 + 8 - 9 = 123448
  123456 + 7 - 8 - 9 = 123446
  ...
  168 possible equations tested

You should not print the same equation more than once. ("1 - 2 - 3 +
456789" is the same as "1 - 2 - 3 + 456789", even if the computer thinks
that the two minus symbols come in a different order.)

Extra Credit: Write your program to accept an arbitrary number and
ordering of digits, an arbitrary set of operators (but allowing the same
operator more than once), and an arbitrary target number that the
equation is supposed to evaluate to.

  [1] 2007 Puzzler Index:
  http://www.cartalk.com/content/puzzler/2007.html [2]
  http://www.cartalk.com/content/puzzler/transcripts/200711/

index.html

#!/usr/bin/env ruby

#requires are only necessary to construct the
#operator permutation table from the list of operators
#if you want to hard code that (and not do the extra credit)
#then no extra libraries are necessary
require 'rubygems'
require_gem 'facets'
require 'facets/core/enumerable/permutation'
require 'enumerator'

Digits= (ARGV.shift || "123456789").split(//)
RequestedResult=(ARGV.shift || 100).to_i
rawoperators=(ARGV.shift || "+--")

#construct the operator permutation table from the list of operators
Operators=rawoperators.split(//).map{|x| " #{x} "}
OperatorPerms=Enumerable::Enumerator.new(Operators,:each_permutation).
   map{|p| p}.uniq

class Array
   
   #Yields all partitionings of the array which have +num+ partitions
   #and retain the order of the elements
   #
   #To relax the ordering constraint, use this in combination
   #with Enumerable#each_permutation
   def each_partition num
      if num==1
   yield [self]
   return self
      end
      (0..length-num).each do |x|
   firstset=self[0..x]
   self[(x+1)..-1].each_partition(num-1) do |y|
      yield [firstset,*y]
   end
      end
      return self
   end
end

#The actual solution to the problem
counter=0
found=0
Digits.each_partition(Operators.length+1) do |digitspart|
   OperatorPerms.each do |operatorsperm|
      counter+=1
      expression=digitspart.zip(operatorsperm).flatten.join
      result=eval(expression)
      puts "************************" if RequestedResult==result
      puts "#{expression} = #{result}"
      puts "************************" if RequestedResult==result
      found+=1 if RequestedResult==result
   end
end
puts "#{counter} possible equations tested"
puts "#{found} equation(s) equaled #{RequestedResult}"

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

=begin

The quiz, then, is to solve this problem without thinking, instead
letting the computer think for you.

I did not intent to seriously submit this solution, but it can be
helpful as an example of how to use Ruby to solve a problem quickly
without thinking too much or locating Knuth on the bookshelve.
Writing this code took maybe ten minutes and happened step-by-step
with checking the immediate results.

Since there are only 168 solutions (254 if you allow a sign before the
first digit), brute-forcing is the simplest thing one can do.
Additional operations can be added by changing the base and mapping
the digits onto further operations. (Different digit order is not
that easy to implement and maybe be futile to do with brute-forcing.)
=end

(00000000..'22222222'.to_i(3)).map { |x| x.to_s(3).rjust(8, "0").
                                                   tr('012', '-+ ') }.
  find_all { |x| x.count("-") == 2 and x.count("+") == 1 }.
  map { |x|
    t = "1" + x.split(//).zip((2..9).to_a).join.delete(" ")
    [eval(t), t]
  }.sort.each { |s, x|
    puts "*****************" if s == 100
    puts "#{x}: #{s}"
    puts "*****************" if s == 100
  }

__END__

2#787<p4>lilith:~/mess/current$ ruby quiz119.rb |grep -C4 100$
123-456-7+89: -251
123+45-67-89: 12
123-45+67-89: 56

···

*****************
123-45-67+89: 100
*****************
12+345-67-89: 201
1-234+567-89: 245
1-23-456+789: 311

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

This solution does the extra credit -- at least for binary operators that are meaningful to ruby eval. It seemed easier to do the extra credit than not.

The fact that the string of digits keeps its order makes this problem much simpler. You don't need to find all of the permutations of digits; you just need to iterate over the string to insert the operators at each legal position in each unique permutation. This solution does that recursively -- which is hopefully easier to understand from looking at insert_ops, which does most of the work, than by putting it into words.

#!/usr/bin/env ruby

···

#

require 'permutation'

class EqGenerator
   def initialize(digits, operators, result)
     @digits = digits
     @operators = operators
     @result = result
   end

   def solve
     # create array of possible solutions, then print, testing against desired result as we go
     correct = 0
     @eqs = permute_ops(@operators).collect { |o| insert_ops(@digits, o) }.flatten
     @eqs.each do |e|
       res = eval(e)
       if (res == @result)
         correct += 1
         puts "***#{e}=#{res}***"
       else
         puts "#{e}=#{res}"
       end
     end
     puts "A total of #{@eqs.length} equations were tested, of which #{correct} " + ((correct == 1)? "was": "were") + " correct"
   end

   private
   def permute_ops(ops)
     # use gem from <http://permutation.rubyforge.org/> to get unique permutations of operators and return as array
     perm = Permutation.new(ops.length)
     return perm.map { |p| p.project(ops) }.uniq
   end

   def insert_ops(digs, ops)
     res = Array.new
     # if only one op to insert, just put it in each available spot and return array of equations
     if ops.length == 1 then
       0.upto(digs.length-2) { |i| res << digs[0..i] + ops + digs[i+1..digs.length]}
     # if more than 1 op, for each legal placement of first op: recursively calculate placements for other ops and digits, then prepend first op
     else
       0.upto(digs.length - (ops.length+1)) { |i| res << insert_ops(digs[i+1..digs.length], ops[1..ops.length]).collect { |e| digs[0..i] + ops[0..0] + e } }
     end
     return res.flatten
   end

end

eg = EqGenerator.new("123456789", "--+", 100)
eg.solve

....................................................................................................
John Browning

here is my first pass:

class Array
  def combinations(n)
    case n
    when 0: []
    when 1: self.map { |e| [e] }
    when size: [self]
    else
      (0..(size - n)).to_a.inject([]) do |mem,i|
        mem += self[(i+1)..size].combinations(n-1).map do |rest|
          [self[i],*rest]
        end
      end
    end
  end
end

equations = 0
separator = "************************"

(1..8).to_a.combinations(3).each do |partitions|
  3.times do |n|
    equation = "123456789"

    partitions.reverse.each_with_index do |partition,index|
      equation = equation.insert(partition, (index == n ? ' + ' : ' -
'))
    end

    result = eval(equation)
    equation << " = #{result}"

    if result == 100
      equation = "#{separator}\n#{equation}\n#{separator}"
    end

    puts equation

    equations += 1
  end
end

puts "#{equations} possible equations tested"

#!/usr/bin/ruby

···

#
# Q119 Solution by Sergey Volkov
# Accept
# - an arbitrary number and ordering of digits,
# - an arbitrary set of operators (but allowing
# the same operator more than once),
# - an arbitrary target number
# Output every possible equation that can be formed,
# and the actual result of that equation.
# The equation that results in target number have
# stars around it.
# At the end, print out the number of formulae
# that were possible.
#
require 'rubygems'
require 'facets/core/enumerable/permutation'

# all possible unique permutations
def op_seqs a
    res = Hash.new
    a.each_permutation{ |pe|
        res[pe] = true
    }
    res.keys
end

# generate all expressions without reordering,
# recursive implementation;
# I could have implemented Array#each_incut( arr )
# to get more generic solution, but I'm too lazy..
# Will it be better to avoid recursion?
# Not required for this quiz, but must for generic method.
# Does anybody knows elegant nonrecursive implementation? Please show
me.
def incut_all digs, opcs, scurr='', &block
    if digs.empty? || opcs.empty?
        # result string
        block[ %/#{scurr}#{digs}#{opcs.pack('C*')}/ ]
        return
    end
    # extend with digit
    incut_all digs[1..-1], opcs, scurr+digs[0].to_s, &block
    # extend with operator
    incut_all digs, opcs[1..-1], scurr+opcs[0].chr, &block
end

# output all possible equations
def show_all digits, opers, target
    # validate arguments
    a_digs = digits.scan(/./).map{ |d| Integer( d ) }
    fail "invalid operator, only [-, +, *, /] allowed" unless %r|^[-
+*/]+| =~ opers
    a_ops = opers.unpack('C*')
    n_targ = Integer( target )

    count = 0 # equation counter
    # operators char set
    op_cs = %/[#{ Regexp.quote a_ops.uniq.pack('C*') }]/
    # Regexp for 'incorrect' expression
    bad_exp_rx = %r/^#{op_cs}|#{op_cs}{2}|#{op_cs}$/o
    for op_seq in op_seqs( a_ops )
        incut_all( a_digs, op_seq ){ |exp|
            next if bad_exp_rx =~ exp
            # beautify expression
            exp.gsub!( %r/#{op_cs}/, %q/ \0 / )
            # evaluate expression
            next unless val = eval( exp ) rescue nil
            s = %/#{exp} = #{val}/
            sep = (val == n_targ) && '*'*s.size
            puts sep if sep
            puts s
            puts sep if sep
            count += 1
        }
    end
    puts %/#{count} possible equations tested/
end

# Arguments accepted:
# an arbitrary number and ordering of digits
digits = ARGV[0] || '123456789'
# an arbitrary set of operators (but allowing the same operator more
than once)
opers = ARGV[1] || '+--'
# an arbitrary target number
target = ARGV[2] || 100

# Output all possible equations
show_all( digits, opers, target )
exit

#=================================================

L:\Ruby\bin\ruby.exe -w L:\rb\Quiz\Q119.rb

...
123-456-7+89 = -251
123-45-678+9 = -591
******************
123-45-67+89 = 100
******************
123-45-6+789 = 861
123-4-5678+9 = -5550
...
168 possible equations tested

My first quiz attempt ever so go easy on me :slight_smile:

From looking at the submissions so far it seems like my solution is

quite verbose but I am proud to have a solution that works! (Extra
Credit Too)

#file permutate.rb
module Permutate

  def Permutate.generate(n)
    permutations = Array.new
    perm = Array.new

    #first permutation
    (1..n).each{|i|
      perm << i
    }

  # print "#{perm}\n"
    permutations << perm.to_s

    (2..(fact(n))).each do |i|
      m = n - 2

      while (perm[m] > perm[m+1])
        m = m - 1
      end
      k = n - 1

      while perm[m] > perm[k]
        k = k - 1
      end
      swap(perm,m,k)
      p = m + 1
      q = n - 1
      while p < q
        swap(perm,p,q)
        p = p + 1
        q = q - 1
      end
  # print "#{perm}\n"
      permutations << perm.to_s
    end
    permutations
  end

  def Permutate.swap(array, a, b)
    temp = array[a]
    array[a] = array[b]
    array[b] = temp
  end

  def Permutate.fact(n)
    return 1 if n == 0
    result = 1
    (2..n).each do |i|
      result *= i
    end
    result
  end

end

#file equation.rb
class Equation

  attr_reader :digits, :rhs, :overlay, :valid

  #digits contains an array of digits in the problem
  #rhs is the right hand side of the equation
  #overlay is a string representation of operators
  # and their position in available positions between
  # digits

  def initialize(digits, rhs, overlay)
    @digits = digits
    @rhs = rhs
    @overlay = overlay
    @eqn = buildEquation

    @valid = isValid?
  end

  def buildEquation
    equation = @digits.to_s

    #overlay permutation string over digits
    #put a space before and after all operators
    (0..@overlay.size).each{|i|
      equation.insert((4*i + 1)," #{@overlay[i,1]} ")
    }

    #take _ placeholders out
    equation.gsub!(" _ ","")

    return equation
  end

  def isValid?
    (eval(@eqn) == @rhs)
  end

  def to_s
    #output the equation in standard format
    result = "#{@eqn} = #{eval(@eqn)}".squeeze(" ")

    if @valid
      marker = "*" * result.size
      return "#{marker}\n#{result}\n#{marker}"
    else
      return result
    end
  end

end

#file equationlist.rb
require 'permutate'
require 'equation'

class EquationList

  attr_reader :digits, :rhs, :operators, :list

  def initialize(digits, operators, rhs)
    @digits = digits
    @operators = operators
    @rhs = rhs
    @list = Array.new
  end

  def build
    #get permutations for location of operators
    perms = Permutate.generate(@digits.size - 1)

    #now assign each operator to a number in the perms list
    operators.each_with_index{|operator,i|
      perms.each{|perm|
        perm.sub!(Regexp.new((i+1).to_s),operator)
      }
    }

    #now replace each number left with _
    #to denote that field is unused
    perms.each{|perm|
      perm.gsub!(/\d/,"_")
    }

    #now we need to get rid of nonunique equations
    perms.uniq!

    #now build a list of equation objects with this information
    perms.each{|perm|
      #puts perm
      @list << Equation.new(@digits,@rhs,perm)
    }
  end

  def display
    puts @list
    puts "#{@list.size} possible equations tested"
  end
end

#file getTo100.rb
require 'equationlist'

digits = %w[1 2 3 4 5 6 7 8 9]
operators = %w[+ - -]
rhs = 100

equations = EquationList.new(digits, ops, rhs)
equations.build
equations.display

Comments are welcome, I'm here to learn!

Matt Hulse
matt.hulse@gmail.com

require 'rubygems'
require 'test/unit'

# http://permutation.rubyforge.org/
require 'permutation'

···

#
# Partitions collections into all possible in-order subsets
#
module Enumerable

   #
   # Generate the partion sizes for a collection of a given
   # length and a specific number of partitions.
   #
   def Enumerable.partition_sizes( collection_length, partition_count, &proc )
     Enumerable.generate_partition_sizes( [], collection_length, partition_count, proc )
   end

   #
   # Create all in-order partitions of the given collection. Each
   # partition should have partition_count elements.
   #
   # For example partitions( [1,2,3], 2 ) would yield
   # [1],[2,3]
   # and [1,2],[3]
   #
   # and partitions( [1,2,3,4], 2 ) would yield
   # [1],[2,3,4]
   # and [1,2],[3,4]
   # and [1,2,3],[4]
   #
   def partitions( partition_count, &proc )
     Enumerable.partition_sizes( self.size, partition_count ) do |partition>
       partitioned_collection = []
       consumed_so_far = 0
       partition.each do |partition_size|
         partitioned_collection << self[ consumed_so_far, partition_size ]
         consumed_so_far += partition_size
       end
       yield partitioned_collection
     end
   end

   private
     def Enumerable.generate_partition_sizes( so_far, length, partition_count, proc )

       raise "Invalid parameter" if( ( partition_count < 1 ) || ( partition_count > length ) )
       partition_size_sum_so_far = so_far.inject(0) { |total,item| total+item }

       if so_far.length == partition_count -1
         working = so_far.dup
         working << length - partition_size_sum_so_far
         proc.call( working )
       else
         up_to = length - partition_size_sum_so_far - (partition_count - so_far.length ) + 1
         for size in 1..( up_to )
           working = so_far.dup
           working << size
           generate_partition_sizes( working, length, partition_count, proc )
         end
       end
     end
end

class PartitionTest < Test::Unit::TestCase
   def test_partition_size_4_count_2
     expected = []
     [1,2,3,4].partitions( 2 ) do |partition|
       expected << partition
     end

     assert_equal expected, [
                              [ [1], [2, 3, 4] ],
                              [ [1, 2], [3, 4] ],
                              [ [1, 2, 3], [4] ]
                            ]
   end

   def test_partition_size_4_count_3
     expected = []
     [1,2,3,4].partitions( 3 ) do |partition|
       expected << partition
     end

     assert_equal expected, [
                             [ [1], [2], [3, 4] ],
                             [ [1], [2, 3], [4] ],
                             [ [1, 2], [3], [4] ]
                            ]
   end

   def test_partition_size_5_count_1
     expected = []
     [1,2,3,4,5].partitions( 1 ) do |partition|
       expected << partition
     end

     assert_equal expected, [
                             [ [ 1, 2, 3,4, 5 ] ],
                            ]
   end

   def test_partition_size_5_count_5
     expected = []
     [1,2,3,4,5].partitions( 5 ) do |partition|
       expected << partition
     end

     assert_equal expected, [
                             [ [1], [2], [3], [4], [5] ],
                            ]
   end

end

def find( digits, operators, magic_number )
   # Generate all possible permutation of operations. Make sure that each operator set
   # begins with an "+" since we are actually creating an equation of
   # "+{term1} {op1}{term2} {op2}{term3}" as it is easier to compute
   operator_permutations = Permutation.for( operators ).map { |p| ( "+" + p.project).split(//) }.uniq

   separator_string = "*" * 20

   total_equations_evaluated = 0

   # Partition the digits into groups of length one more than the number of operators
   digits.partitions( operators.length + 1 ) do |digit_partition|

     # For each operator permutation we'll compute the result of mixing the operators
     # between the current partition
     operator_permutations.each do |operator_permutation|

       # Match up the digit partition and the operators
       equation = digit_partition.zip( operator_permutation )

       # Create the string representation, joining operators
       # and operands into a resulting equation.
       equation_string = equation.inject("") do |result,term|
         # Only add the operator if we have something in the string
         # this strips out the initial dummy "+" operator from our
         # equation.
         result = result + " " + term[1] + " " unless result.empty?
         result = result + term[0].join
       end

       # Evaluate the equation
       equation_value = eval( equation_string )
       total_equations_evaluated += 1

       # Output as required with stars surrounding any
       # equation that yielded the value we wanted
       puts separator_string if equation_value == magic_number
       puts "#{equation_string} = #{equation_value}"
       puts separator_string if equation_value == magic_number
     end
   end
   puts "#{total_equations_evaluated} possible equations tested"
end

digits = [1,2,3,4,5,6,7,8,9]
operators = "--+"
find( digits, operators, 100 )

James Gray wrote:

The three rules of Ruby Quiz:

=] Here is my solution.

You will forgive the extension of the file, but this quiz was an excuse
for me to learn and play with new things, like OptParse. I rather
uploaded it in a pastie.

My solution handles extra credits, and my own suggestions except
parenthesis precedence. With full options, it runs very slow, because
the number of permutations and combinations grows n!

As an example, a run with these options

$ ruby quiz119.rb --o +,-,*,/ --f --m --v --d 1234567

produces

123+4+5*0.67 = 130.35
123+4+5*6.7 = 160.50
0.123+4+5*67 = 339.12
.....

17 times target reached, out of 217224 equations tested:
123*4-56*7 = 100.00
1+23+4+5+67 = 100.00
1+2+34+56+7 = 100.00
1+23-4+56/0.7 = 100.00
12+0.3*45*6+7 = 100.00
12+3*4.5*6+7 = 100.00
12+3*45*0.6+7 = 100.00
-1-234+5*67 = 100.00
1*23*4+56/7 = 100.00
-1/2+34-0.5+67 = 100.00
-1+0.2*34*5+67 = 100.00
-1+2*3.4*5+67 = 100.00
-1+2*34*0.5+67 = 100.00
1*23*4-5+6+7 = 100.00
-1-2+34*5-67 = 100.00
-1/2+3/0.4/5*67 = 100.00
-1/2+3/4/0.5*67 = 100.00
Total run time: 50.844356 seconds

Trying to do that for digits 1 to 9 is a craziness @.@

Here is the file:

http://pastie.caboo.se/52702

···

--
Posted via http://www.ruby-forum.com/\.

My first posted solution - hope it's ok!

## Start target.rb
class TargetFinder
  attr_reader :inputs, :operators

  def initialize(inputs, operators)
    self.inputs = inputs
    self.operators = operators
    reset
  end

  # Clear out cached, calculated data
  def reset
    @equations = nil
    @results = nil
  end

  # Only allow digits as input
  def inputs= new_inputs
    @inputs = new_inputs.gsub /\D/, ''
    reset
  end

  # Only the +, - and * operators are really safe to use with this approach
  def operators= new_ops
    @operators = new_ops.gsub(/[^+*-]/, '').split(//)
    reset
  end
  
  # Loop through our results putting the required stars around the correct lines
  def get_to target
    calculate if @results.nil?
    @results.each do |eq, result|
      puts "*" * 30 if result == target.to_i
      puts "#{eq} = #{result}"
      puts "*" * 30 if result == target.to_i
    end

    puts "%d equations tested" % @equations.length
  end

  # Calculate all of the possible equations given a set of inputs
  def calculate
    @equations = self.class.permutate(@inputs, @operators)
    @results = {}
    @equations.each do |eq|
      @results[eq] = eval(eq)
    end
  end

  # Here's the workhorse, recursively calculates all possible equations from an
input string and operators
  def self.permutate(inputs, operators)
    return [inputs] if operators.empty?
    arr = []
    # Loop through all the possible 'first' value/operator pairs
    operators.uniq.each do |op|
      other_operators = operators.without(op)
      (1..inputs.length-operators.length).each do |i|

        # Find all possible endings from the remaining inputs and operators, and
prepend this beginning to all of them
        permutate(inputs[i..-1], other_operators).each do |permutation|
          arr << "#{inputs[0...i]} #{op} #{permutation}"
        end

      end
    end
    arr
  end
end

# A long-winded way of removing a single item from an array which may have
duplicates
# Almost certainly not the best way of doing this but good enough
class Array
  def without item
    new_array = []
    found = false
    each do |x|
      if x == item
        new_array << x if found
        found = true
        next
      end
      new_array << x
    end
    new_array
  end
end

if $0 == __FILE__
  inputs = ARGV.shift || "123456789"
  target = ARGV.shift || "100"
  operators = ARGV.shift || "+--"

  finder = TargetFinder.new(inputs, operators)
  finder.get_to target
end

Note:
Like I said in my submission to #120, this seems not to have gotten through
when I sent it last week, and I just noticed. So here it is again (though
I have changed it a bit since then - originally I had an Operation called
Cat that concatenated adjacent numbers; now that's done separately).

···

------------------------

Like my submission for last week's quiz, this one grew too long. But there's
some things I like about it, so here it is. First I wrote an algorithm
to generate all permutations of an Array, but excluding those that are the
same as others ('--+' and '--+' won't both be generated). To do this, for each
unique element of the Array, I append it to all results of running it
recursively on the rest (if that's worded confusingly, look at the code at the
top of array_stuff.rb).

I then build an Operator class, which is just a Proc with a name so to_s can
be, say, '+' instead of '#<Proc:0xb7acef88...>'. All Operators sit inside an
Array, and take two arguments: the Array they're in, and their index within
that Array. When called, they modify the Array. For example:

  a = [1, Add, 2]

The Add Operator can then be called by either of these:

  Add[a, 1]
  a.exec_op_at!(1)

and afterwards a == [3]. First I build an Array containing all the initial
Operators (Sub, Sub, Add). This Array is what I call each_uniq_permutation on.
Each perm then gets mixed in to the data by the each_mix method in array_stuff.

Example:

  nums = [1, 2, 3]
  ops = [Add, Sub]

ops.each_uniq_permutation will yield [Add, Sub] and [Sub, Add]. Then calling:

  nums.each_mix(ops)

will yield each of:
  1 2 Add Sub 3
  1 Add 2 Sub 3
  1 Add Sub 2 3
  ... etc etc

Some will be valid expressions, many won't.

Each Operator also has a precedence. For each Operator in the
Array from highest to lowest precedence, we let it modify the Array.

   [1, Add, 2, Mult, 2, 3]
-> [1, Add, 2, Mult, 23] # concat adjacent numbers
-> [1, Add, 46] # Mult has higher precedence than Add
-> [47]

One thing I don't like about my code is that I scan through the expression
Arrays a lot, like for finding the next adjacent numbers to concatenate or the
next Operator to apply. Ops can have arbitrary effects on the Array, so we need
to watch everything. Maybe an Op expands to other Ops or something. I thought
of implementing this in different ways, like specifically keeping an
ordered-by-precedence list of all Ops in the expression, but didn't get to it.
Oh, and I've implemented Ops for addition, subtraction, multiplication,
division, and parenthesis.

Here's the code:

# ---------------------------------------------------------------------------
# array_stuff.rb
# Ruby Quiz 119: Getting to 100

class Array
  # Yield once for each unique element of this Array.
  def each_uniq_element
    self.uniq.each { |e| yield e }
  end

  # Like Array delete(), but only delete a single occurrence of e instead of
  # all of them. Also unlike delete(), it returns a new Array instead of
  # modifying this one (why isn't delete() named delete!() ?)
  def delete_one(e)
    arr = self.clone
    i = arr.index(e)
    arr.delete_at(i) if not i.nil?
    arr
  end

  # Generates all unique permutations of this Array, yielding each one.
  # By 'unique' I mean both permutations [...,x,...,y,...] and [...,y,...,x,...]
  # will not be generated if x == y.
  # (wonder if there's a non-recursive way to do this?)
  def each_uniq_permutation
    if self.size.zero?
      yield []
    else
      self.each_uniq_element do |e|
        self.delete_one(e).each_uniq_permutation do |perm_part|
          yield([e] + perm_part)
        end
      end
    end
  end

  # Find the lowest index of val, starting from index start.
  def index_past(val, start)
    (start...self.size).each { |i| return i if self[i] == val }
    nil
  end

  # Replace all values from indices i through j (inclusive) with the single
  # value val.
  def replace_at!(i, j, val)
    raise 'Bad indices given' if i < 0 or j < i or j >= self.size
    self.slice!(i, j-i+1)
    self.insert(i, val)
  end

  # "Mix together" self and arr in all possible ways that preserve the order of
  # each, yielding the results.
  def each_mix(arr)
    if self.empty? then yield arr.clone
    elsif arr.empty? then yield self.clone
    else
      self.slice(1, self.length).each_mix(arr) do |mix|
        yield [self.first] + mix
      end
      self.each_mix(arr.slice(1, arr.length)) do |mix|
        yield [arr.first] + mix
      end
    end
  end
end

# ---------------------------------------------------------------------------
# ops.rb
# Ruby Quiz 119: Getting to 100

require 'array_stuff'
require 'enumerator'

class Symbol
  # This nice ol' trick.
  def to_proc
    proc { |obj, *args| obj.send(self, *args) }
  end
end

class Array
  # Return the index of the next-highest-precendence operator within this Array.
  def next_op_index
    # Yuck... a linear search.
    op_i = nil
    self.each_index do |i|
      if self[i].is_a?(Proc) and
           (op_i.nil? or
           Ops.precedence_of(self[i]) > Ops.precedence_of(self[op_i]))
        op_i = i
      end
    end
    op_i
  end

  # Execute the operation that is at the given index on self.
  def exec_op_at!(index)
    raise 'Not a Proc' if not self[index].is_a? Proc
    self[index][self, index] # I like this line..
  end

  # Concatenate any adjacent numbers. Repeat until none are left.
  def concat_nums!(base = 10)
    # There's gotta be a much, much better way to do this...
    i = 0
    while i < self.size-1
      while self[i].is_a? Numeric and self[i+1].is_a? Numeric
        # Would logs & exponents be better than strings for this?
        self[i] = (self[i].to_s(base) + self[i+1].to_s(base)).to_i(base)
        self.delete_at(i+1)
      end
      i += 1
    end
  end

  # Process all operators in self, from highest-precedence to lowest. Along the
  # way, adjacent numbers will be concatenated (which always has higher
  # precedence than any operators).
  def process_ops!
    concat_nums!
    op_i = self.next_op_index
    while not op_i.nil?
      self.exec_op_at! op_i
      concat_nums!
      op_i = self.next_op_index
    end
    self
  end

  # Just like process_ops!, but return a new Array instead of working on self.
  def process_ops
    arr = self.clone
    arr.process_ops!
    arr
  end
end

module Ops
  # Here lie blocks of incense, burning for the Proc God.
  # Inhale.

  # Proc with a name for prettier to_s.
  class Operator < Proc
    def initialize(name)
      super() { yield }
      @name = name.to_s
    end
    def to_s; @name; end
  end

  # Produces an Operator that sits between two numbers and replaces them.
  # For example, [1,Add,2] => [3]
  # Its name will be op.to_s.
  BinaryOp = lambda do |op|
    Operator.new(op.to_s) do |list, index|
      num_left, num_right = list[index-1], list[index+1]
      raise 'Not numeric.' if not num_left.is_a? Numeric or
                              not num_right.is_a? Numeric
      list.replace_at!(index-1, index+1, op.to_proc[num_left, num_right])
    end
  end

  # Operators that sit between two numbers and perform arithmetic on them.
  Mult = BinaryOp[:*]
  Div = BinaryOp[:/]
  Add = BinaryOp[:+]
  Sub = BinaryOp[:-]

  # Operator to group things.
  LeftParen = Operator.new('(') do |list, left_paren_index|
    right_paren_index = list.index_past(RightParen, left_paren_index+1)
    raise 'No right paren' if right_paren_index.nil?
    contained = list.slice!(left_paren_index,
                            right_paren_index - left_paren_index + 1)
    contained.shift; contained.pop # Remove parens on ends
    contained.process_ops!
    list.insert(left_paren_index, *contained)
  end

  # Does nothing; LeftParen takes care of everything.
  RightParen = Operator.new(')') { |list, index| }

  # Precedence of operators.
  Precedence = {
    LeftParen => 3,
    RightParen => 2,
    Mult => 1,
    Div => 1,
    Add => 0,
    Sub => 0
  }

  # Get the precedence of the given Operation. Higher is more important.
  def Ops.precedence_of(op)
    Precedence[op]
  end
end

# ---------------------------------------------------------------------------
# getting_to_100.rb
# Ruby Quiz 119: Getting to 100

require 'array_stuff'
require 'ops'

# Main method for this Quiz. Print out one line for each valid way of arranging
# nums and ops into an expression. The marker will appear around results
# matching target.
#
# Arguments:
# nums: Something that, when to_a is called on it, returns an Array of
# numbers.
# ops: An Array of Operators.
# target: Target number.
# marker: String to print above & below target hits.
def get_to(nums, ops, target = 100, marker = '************************')
  nums = nums.to_a
  num_tested = num_valid = num_found = 0

  # Permute the ops in all uniq ways, and mix them with nums to generate
  # expressions.
  ops.each_uniq_permutation do |op_perm|
    nums.each_mix(op_perm) do |expr|
      num_tested += 1
      begin
        result = expr.process_ops
        num_valid += 1

        if result.size == 1
          if result.first == target
            num_found += 1
            puts marker
            puts "#{num_valid}: #{expr.join} = #{result.join(',')}"
            puts marker
          else
            puts "#{num_valid}: #{expr.join} = #{result.join(',')}"
          end
        else
          # The list didn't collapse all the way to a single element.
          #$stderr.puts 'Warning: operation did not collapse:'
          #$stderr.puts "#{num_tested}: #{expr.join} = #{result.join(',')}"
        end
      rescue Object => ex
        # Some operation didn't work. Perhaps non-matching parens. Maybe this
        # should be handled another way..
        #$stderr.puts 'Warning: operation failed.'
        #$stderr.puts ex
      end
    end
  end

  puts '----------------'
  puts "#{num_tested} possible expression were generated."
  puts " Of those, #{num_valid} were valid."
  puts " Of those, #{num_found} matched the target."
end

# Example from the Quiz.
#get_to( (1..9), [Ops::Sub, Ops::Sub, Ops::Add] )

# Example showing off parenthesis.
#get_to( (1..3), [Ops::Add, Ops::Mult, Ops::LeftParen, Ops::RightParen], 9 )

# ---------------------------------------------------------------------------

--
Jesse Merriman
jessemerriman@warpmail.net
http://www.jessemerriman.com/

We had that quiz, though this one is definitely similar:

http://www.rubyquiz.com/quiz7.html

James Edward Gray II

···

On Apr 6, 2007, at 8:32 AM, Robert Dober wrote:

sorry for being picky but I guess it might be useful
- an arbitrary set of operators
+ an array of operators that can come in any order
hopefully somebody will find the correct formal expression to describe
the beast ...
, and an arbitrary target number that the equation is supposed to evaluate

Not as described, in the original quiz, but I'd say...sure! :slight_smile: It
would require that it either be applied before the first digit:
  -12345 - 67 + 89
or after another operator:
  123 + -45 -6789
but I don't see any reason to disallow that sort of extra logic if you
want to do it.

···

On Apr 6, 1:53 pm, Carlos <a...@quovadis.com.ar> wrote:

[Ruby Quiz <j...@grayproductions.net>, 2007-04-06 14.55 CEST]
[...]

> The NPR show "Car Talk" has regular quizzes that they call "Puzzlers"[1]. The
> one listed on their web site for March 12th[2] is titled "Getting to 100". In
> the quiz, you are supposed to write down the digits 1-9 in order, followed by "
> = 100", and then insert between them two minus symbols and one plus symbol (in
> any order) to make the formula correct. You aren't allowed to re-arrange digits,
> or do some toothpick math like combine two minus signs to make a plus. You must
> use every digit, and all three operators.

Can the plus and minus symbols be used as unary operators?

My submission uses extreme (in)elegance :wink: in accordance with this
weeks quiz saying "don't think, let the computer think for you"

I'm submitting several versions, one full version that includes the
extra credit, and several "golf" versions

Enjoy!

--Kyle

First we have this version
#########begin########### 42 lines long
elegance = nil

···

#########################

to100 = Hash.new()

@setlength=9#set, starting from 1 to use

@maxops=@setlength-1#the maximum number of operators (including space)
in any equation

@operator = ["", "+", "-"]

@oplength = @operator.length

keylength = @oplength.power!(@maxops)

def bogoGen()

  little=Array.new(@setlength+@maxops) do

    >i>

    if i.modulo(2)==0 then

      i=i/2

      i+=1

    else

      i=@operator[rand(@oplength)]

    end

  end

  return(little.join)

end

writingHamlet = Time.now()

while to100.keys.length<keylength

  elegance = bogoGen()

  to100.store(elegance,eval(elegance))

  #puts "Found #{to100.keys.length} formulas" if to100.keys.length%100==0

end

millionMonkeys = Time.now()

to100.sort.each do

  >answer>

  fortytwo=answer[1]==100?'*':' '

  #display the answer!

  puts "#{fortytwo} #{answer[0]}=#{answer[1]} #{fortytwo}"

end

willFinish = Time.now()

#puts "Total calculation time: #{millionMonkeys - writingHamlet} seconds"

#puts "Total run time: #{willFinish - writingHamlet} seconds"
#####end ######

#compact v1
t={}

while t.length<(6561)

  e = Array.new(17){|i| i=i%2==0?i/2+1:["","+","-"][rand(3)]}.join

  t.store(e,eval(e))

end

t.sort.each {|a| f=a[1]==100?'*':' '; puts "#{f} #{a[0]}=#{a[1]} #{f}"}

#compact v2
t={}

while t.length<(6561)

  t.store(Array.new(17){|i| i=i%2==0?i/2+1:["","+","-"][rand(3)]}.join,'')

end

t.sort.each {|a| f=eval(a[0])==100?'*':' '; puts "#{f}
#{a[0]}=#{eval(a[0])} #{f}"}

#compact v3
t=Array.new(6561){|i|i}

t.each_index{|a| while t[a]=Array.new(17){|i|
i=i%2==0?i/2+1:["","+","-"][rand(3)]}.join and not
t.length==t.uniq.length;end}

t.sort.each {|a| f=eval(a)==100?'*':' '; puts "#{f} #{a}=#{eval(a)} #{f}"}

Here is my solution. Again I've used Test::Unit to test my solution,
as well as providing a fairly nice command-line operation. I too did
the extra credit (I actually coded it that from the beginning, seemed
easy enough.)

I borrowed some code for the operator permutations, but after writing
my own code to create the numerical groups for the equations I
wondered if it might be possible to use one basic algorithm for both.
But I don't have time to fix this up at the moment. I may submit
another solution later this week.

# Based on:
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/139290
# Author: Endy Tjahjono
class String
  def perm
    return [self] if self.length < 2
    ret = []

    0.upto(self.length - 1) do |n|
      rest = self.split(//u) # for UTF-8 encoded strings
      picked = rest.delete_at(n)
      rest.join.perm.each { |x| ret << picked + x }
    end

    ret
  end
end

# All the rest is Ryan Leavengood code :slight_smile:
class EquationSolver
  attr_reader :num_seq, :operators, :result

  def initialize(num_seq = "123456789", operators = "--+", result_wanted = 100)
    @num_seq, @operators, @result_wanted = num_seq, operators, result_wanted
  end

  SEP = '*' * 24

  def solve
    equations = 0
    ops = op_combinations(@operators).map{|a| a.split('')}
    generate_groups(@num_seq).each do |group|
      ops.each do |op|
        eq = group.zip(op).join(' ')
        result = eval(eq)
        puts SEP if result == @result_wanted
        puts "#{eq}= #{result}"
        puts SEP if result == @result_wanted
        equations += 1
      end
    end
    puts "#{equations} possible equations tested"
  end

  def op_combinations(operators)
    operators.perm.uniq
  end

  # Returns an array of numeric strings representing how the given
  # number can be split into the given number of groups
  def num_split(number, num_groups)
    return [number.to_s] if num_groups == 1
    return ["1" * num_groups] if number == num_groups
    result = []
    ((number + 1)-num_groups).times do |i|
      cur_num = i + 1
      num_split(number - cur_num, num_groups - 1).each do |group|
        result << "#{cur_num}#{group}"
      end
    end
    result
  end

  def generate_groups(num_seq, num_groups = @operators.length+1)
    num_split(num_seq.length, num_groups).map do |split_on|
      # Turn the result from num_split into a regular expression,
      # with each number becoming grouped dots
      reg_exp = split_on.split('').map{|n| "(#{'.' * n.to_i})"}.join
      num_seq.scan(/#{reg_exp}/).first
    end
  end
end

require 'test/unit'

class SolverTester < Test::Unit::TestCase
  def setup
    @es = EquationSolver.new
  end

  def test_string_perm
    assert_equal(["1"],
      "1".perm)
    assert_equal(["12","21"],
      "12".perm)
    assert_equal(["123", "132", "213", "231", "312", "321"],
      "123".perm)
  end

  def test_op_combinations
    assert_equal(["1"],
      @es.op_combinations("1"))
    assert_equal(["12","21"],
      @es.op_combinations("12"))
    assert_equal(["123", "132", "213", "231", "312", "321"],
      @es.op_combinations("123"))
    assert_equal(["223", "232", "322"],
      @es.op_combinations("223"))
    assert_equal(["--+", "-+-", "+--"],
      @es.op_combinations("--+"))
  end

  def test_num_split
    assert_equal(["11"],
      @es.num_split(2,2))
    assert_equal(["111"],
      @es.num_split(3,3))
    assert_equal(["12", "21"],
      @es.num_split(3,2))
    assert_equal(["13", "22", "31"],
      @es.num_split(4,2))
    assert_equal(["112", "121", "211"],
      @es.num_split(4,3))
  end

  def test_generate_groups
    assert_equal([["1", "2", "34"], ["1", "23", "4"], ["12", "3", "4"]],
      @es.generate_groups("1234", 3))
  end
end

if $0 == __FILE__
  # By default do not run the test cases
  Test::Unit.run = true

  if ARGV.length > 0
    if ARGV[0] == 'ut'
      # Run the test cases
      Test::Unit.run = false
    else
      begin
        if ARGV.length != 3
          raise
        else
          if ARGV[0] =~ /^\d*$/ and
            ARGV[1] =~ /^[\+\-\*\/]*$/ and
            ARGV[2] =~ /^-?\d*$/

            EquationSolver.new(ARGV[0], ARGV[1], ARGV[2].to_i).solve
          else
            raise
          end
        end
      rescue
        puts "Usage: #$0 <number sequence> <string of operators>
<result wanted>"
        puts "\tOr just the single parameter 'ut' to run the test cases."
        exit(1)
      end
    end
  else
    # Solve the default case
    EquationSolver.new.solve
  end
end
__END__

Regards,
Ryan