[QUIZ] Getting to 100 (#119)

K

Ken Bloom

My submission uses extreme (in)elegance ;) 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}"}

For those reading along, the magic number 6560=='22222222'.to_i(3)
His goal is to just generate 6560 different combinations of the operators
using a random generator.

One should note that this technically violates the rules of code golf
which require the solution to terminate within a short period of time (I
believe 4 seconds), so that its correctness can be verified automatically
without having to worry about DOS attacks.
 
R

Ryan Leavengood

(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
}

That's brilliant. It took me a minute to figure out how it worked, but I like.

I started working on a generalized solution using this algorithm, but
it gets tricky. It isn't too hard to generalize the number sequence,
but generalizing the operators really makes it messy.

Ryan
 
R

Ryan Leavengood

I started working on a generalized solution using this algorithm, but
it gets tricky. It isn't too hard to generalize the number sequence,
but generalizing the operators really makes it messy.

OK, I figured out the generalized solution based on Christian's submission:

seq = ARGV[0]
ops = ARGV[1]
res = ARGV[2].to_i
uops = ops.split('').uniq
print (0..(uops.length.to_s * (seq.length-1)).to_i(uops.length+1)).
map { |x| x.to_s(uops.length+1).rjust((seq.length-1), '0').
tr('012345', uops.join.ljust(6, ' ')) }.
find_all { |x| uops.inject(true){|b, op| b and (x.count(op) ==
ops.count(op))} }.
map { |x|
t = seq[0,1] + x.split('').zip(seq[1..-1].split('')).join.delete(' ')
[eval(t), t]
}.each { |s, x|
puts "*****************" if s == res
puts "#{x}: #{s}"
puts "*****************" if s == res
}.size
puts " possible equations tested"

This supports any arbitrary sequence of numbers, up to 5 different
operators in whatever combination, and whatever result. It gets slower
as you add operators though.

This was fun, but I can just see how slow it is, especially with the
inject I added.

As you can see I also added printing how many equations were tested.

Ryan
 
C

Carl Porth

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,*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"
 
C

Carl Porth

After going back and reading the current solutions, I like Ken Bloom's
each_partition method. It's much cleaner than my combinations method.
 
V

vsv

#!/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
 
M

Matt Hulse

My first quiz attempt ever so go easy on me :)
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
array = 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, :eek:verlay, :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
([email protected]).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, :eek:perators, :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
(e-mail address removed)
 
M

Marcel Ward

After going back and reading the current solutions, I like Ken Bloom's
each_partition method. It's much cleaner than my combinations method.

I agree, very clean and efficient. The three lines that put it all
together using #zip are what makes this work for me:

Digits.each_partition(Operators.length+1) do |digitspart|
OperatorPerms.each do |operatorsperm|
expression=digitspart.zip(operatorsperm).flatten.join
...

I think I would be feeling very happy if I'd submitted this solution :)
 
G

Gavin Stark

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 )
 
H

Harrison Reiser

Wow, this is pretty freaky. My solution is eerily similar to Ken's
(albeit less concise), and I hadn't seen his code until now. Look:


I wrote an Array#each_partition too, and my first implementation of it
looked almost just like his:

def each_partition(n = length)
if n < 1
yield []
else
(0..length-n).each do |x|
section=self[x, length]
self[0, x].each_partition(n-1) do |part|
yield part << section
end
end
end
self
end

I rewrote this when I found an iterative algorithm for it, but I
hadn't realized that that seemingly extraneous argument could be used
to limit the number of partitions, which is clever. Still, this is a
fundamental algorithm in combinatorics, so I'm not too surprised that
he chose to put in Array. This is where it gets eerie:
I agree, very clean and efficient. The three lines that put it all
together using #zip are what makes this work for me:

Digits.each_partition(Operators.length+1) do |digitspart|
OperatorPerms.each do |operatorsperm|
expression=digitspart.zip(operatorsperm).flatten.join
...

my version of the above:

digits.send(digits_message) do |part|
...
operators.send(*ops_send_args) do |tuple|
expr = part.zip(tuple).join(' ')
...

And then I go on to eval(expr), as Ken did. Take it for granted that
my uses of send() do essentially the same thing. Weird, no?
I think I would be feeling very happy if I'd submitted this solution :)

I was, too, before I saw Ken's.

Harrison
 
R

Raj Sahae

Christian said:
=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
I'm sorry to be a noob on this, but can someone please explain to me
what this is doing. If it works, it must be genius, and I can't figure
it out.

Raj Sahae
 
R

Robert Dober

Christian said:
=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
I'm sorry to be a noob on this, but can someone please explain to me
what this is doing. If it works, it must be genius, and I can't figure
it out.

Raj Sahae
Actually I have no idea whatsoever, this is normally a good starting point ;)
irb is our friend of course, so let us hack away:
irb(main):003:0> (000..'222'.to_i(3)).map{|x|x.to_s(3).rjust(3,
"0").tr('012', '-+ ')}
=> ["---", "--+", "-- ", "-+-", "-++", "-+ ", "- -", "- +", "- ",
"+--", "+-+", "+- ", "++-", "+++", "++ ", "+ -", "+ +", "+ ", " --",
" -+", " - ", " +-", " ++", " + ", " -", " +", " "]

Aha the ternary array is used to create all kind of operator
combinations including " ".
I do not know exactly what this is good for right now, but I guess we
will learn.
As a next step I increase 3 to 8 as I think we can understand that now
and I will add the next method
(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 }
=> ["--+ ", "-- + ", "-- + ", "-- + ", "-- + ", "--
+", "-+- ", "-+ - ", "-+ - ", "-+ - ", "-+ - ", "-+
-", "- -+ ", "- - + ", "- - + ", "- - + ", "- - +", "-
+- ", "- + - ", "- + - ", "- + - ", "- + -", "- -+ ",
"- - + ", "- - + ", "- - +", "- +- ", "- + - ", "- + -
", "- + -", "- -+ ", "- - + ", "- - +", "- +- ", "- +
- ", "- + -", "- -+ ", "- - +", "- +- ", "- + -", "-
-+", "- +-", "+-- ", "+- - ", "+- - ", "+- - ", "+-
- ", "+- -", "+ -- ", "+ - - ", "+ - - ", "+ - - ", "+
- -", "+ -- ", "+ - - ", "+ - - ", "+ - -", "+ -- ",
"+ - - ", "+ - -", "+ -- ", "+ - -", "+ --", " --+
", " -- + ", " -- + ", " -- + ", " -- +", " -+- ", " -+ -
", " -+ - ", " -+ - ", " -+ -", " - -+ ", " - - + ", " - -
+ ", " - - +", " - +- ", " - + - ", " - + - ", " - + -", " -
-+ ", " - - + ", " - - +", " - +- ", " - + - ", " - + -", " -
-+ ", " - - +", " - +- ", " - + -", " - -+", " - +-", "
+-- ", " +- - ", " +- - ", " +- - ", " +- -", " + -- ",
" + - - ", " + - - ", " + - -", " + -- ", " + - - ", " + -
-", " + -- ", " + - -", " + --", " --+ ", " -- + ", " --
+ ", " -- +", " -+- ", " -+ - ", " -+ - ", " -+ -", " -
-+ ", " - - + ", " - - +", " - +- ", " - + - ", " - + -", "
- -+ ", " - - +", " - +- ", " - + -", " - -+", " - +-", "
+-- ", " +- - ", " +- - ", " +- -", " + -- ", " + - - ",
" + - -", " + -- ", " + - -", " + --", " --+ ", " -- +
", " -- +", " -+- ", " -+ - ", " -+ -", " - -+ ", " - -
+", " - +- ", " - + -", " - -+", " - +-", " +-- ", " +-
- ", " +- -", " + -- ", " + - -", " + --", " --+ ", "
-- +", " -+- ", " -+ -", " - -+", " - +-", " +-- ", "
+- -", " + --", " --+", " -+-", " +--"]

It is a little longer but we see already that only 2 minuses and 1
plus is allowed...
By storing this into a variable tmp we can continue easily to explore
what is happening
tmp.map{|x| t = "1" + x.split(//).zip((2..9).to_a).join.delete(" ") ;
[eval(t),t]}
=> [[456785, "1-2-3+456789"], [56754, "1-2-34+56789"], [6443,
"1-2-345+6789"], [-2668, "1-2-3456+789"], [-34479, "1-2-34567+89"],
[-345670, "1-2-345678+9"], [-456787, "1-2+3-456789"], [-56756,
"1-2+34-56789"], [-6445, "1-2+345-6789"], [2666, "1-2+3456-789"],
[34477, "1-2+34567-89"], [345668, "1-2+345678-9"], [56763,
"1-23-4+56789"], [6722, "1-23-45+6789"], [311, "1-23-456+789"],
[-4500, "1-23-4567+89"], [-45691, "1-23-45678+9"], [-56807,
"1-23+4-56789"], [-6766, "1-23+45-6789"], [-355, "1-23+456-789"],
[4456, "1-23+4567-89"], [45647, "1-23+45678-9"], [6551,
"1-234-5+6789"], [500, "1-234-56+789"], [-711, "1-234-567+89"],
[-5902, "1-234-5678+9"], [-7017, "1-234+5-6789"], [-966,
"1-234+56-789"], [245, "1-234+567-89"], [5436, "1-234+5678-9"],
[-1561, "1-2345-6+789"], [-2322, "1-2345-67+89"], [-3013,
"1-2345-678+9"], [-3127, "1-2345+6-789"], [-2366, "1-2345+67-89"],
[-1675, "1-2345+678-9"], [-23373, "1-23456-7+89"], [-23524,
"1-23456-78+9"], [-23537, "1-23456+7-89"], [-23386, "1-23456+78-9"],
[-234565, "1-234567-8+9"], [-234567, "1-234567+8-9"], [-456789,
"1+2-3-456789"], [-56820, "1+2-34-56789"], [-7131, "1+2-345-6789"],
[-4242, "1+2-3456-789"], [-34653, "1+2-34567-89"], [-345684,
"1+2-345678-9"], [-56769, "1+23-4-56789"], [-6810, "1+23-45-6789"],
[-1221, "1+23-456-789"], [-4632, "1+23-4567-89"], [-45663,
"1+23-45678-9"], [-6559, "1+234-5-6789"], [-610, "1+234-56-789"],
[-421, "1+234-567-89"], [-5452, "1+234-5678-9"], [1551,
"1+2345-6-789"], [2190, "1+2345-67-89"], [1659, "1+2345-678-9"],
[23361, "1+23456-7-89"], [23370, "1+23456-78-9"], [234551,
"1+234567-8-9"], [56794, "12-3-4+56789"], [6753, "12-3-45+6789"],
[342, "12-3-456+789"], [-4469, "12-3-4567+89"], [-45660,
"12-3-45678+9"], [-56776, "12-3+4-56789"], [-6735, "12-3+45-6789"],
[-324, "12-3+456-789"], [4487, "12-3+4567-89"], [45678,
"12-3+45678-9"], [6762, "12-34-5+6789"], [711, "12-34-56+789"], [-500,
"12-34-567+89"], [-5691, "12-34-5678+9"], [-6806, "12-34+5-6789"],
[-755, "12-34+56-789"], [456, "12-34+567-89"], [5647, "12-34+5678-9"],
[450, "12-345-6+789"], [-311, "12-345-67+89"], [-1002,
"12-345-678+9"], [-1116, "12-345+6-789"], [-355, "12-345+67-89"],
[336, "12-345+678-9"], [-3362, "12-3456-7+89"], [-3513,
"12-3456-78+9"], [-3526, "12-3456+7-89"], [-3375, "12-3456+78-9"],
[-34554, "12-34567-8+9"], [-34556, "12-34567+8-9"], [-56778,
"12+3-4-56789"], [-6819, "12+3-45-6789"], [-1230, "12+3-456-789"],
[-4641, "12+3-4567-89"], [-45672, "12+3-45678-9"], [-6748,
"12+34-5-6789"], [-799, "12+34-56-789"], [-610, "12+34-567-89"],
[-5641, "12+34-5678-9"], [-438, "12+345-6-789"], [201,
"12+345-67-89"], [-330, "12+345-678-9"], [3372, "12+3456-7-89"],
[3381, "12+3456-78-9"], [34562, "12+34567-8-9"], [6903,
"123-4-5+6789"], [852, "123-4-56+789"], [-359, "123-4-567+89"],
[-5550, "123-4-5678+9"], [-6665, "123-4+5-6789"], [-614,
"123-4+56-789"], [597, "123-4+567-89"], [5788, "123-4+5678-9"], [861,
"123-45-6+789"], [100, "123-45-67+89"], [-591, "123-45-678+9"], [-705,
"123-45+6-789"], [56, "123-45+67-89"], [747, "123-45+678-9"], [-251,
"123-456-7+89"], [-402, "123-456-78+9"], [-415, "123-456+7-89"],
[-264, "123-456+78-9"], [-4443, "123-4567-8+9"], [-4445,
"123-4567+8-9"], [-6667, "123+4-5-6789"], [-718, "123+4-56-789"],
[-529, "123+4-567-89"], [-5560, "123+4-5678-9"], [-627,
"123+45-6-789"], [12, "123+45-67-89"], [-519, "123+45-678-9"], [483,
"123+456-7-89"], [492, "123+456-78-9"], [4673, "123+4567-8-9"], [2012,
"1234-5-6+789"], [1251, "1234-5-67+89"], [560, "1234-5-678+9"], [446,
"1234-5+6-789"], [1207, "1234-5+67-89"], [1898, "1234-5+678-9"],
[1260, "1234-56-7+89"], [1109, "1234-56-78+9"], [1096,
"1234-56+7-89"], [1247, "1234-56+78-9"], [668, "1234-567-8+9"], [666,
"1234-567+8-9"], [444, "1234+5-6-789"], [1083, "1234+5-67-89"], [552,
"1234+5-678-9"], [1194, "1234+56-7-89"], [1203, "1234+56-78-9"],
[1784, "1234+567-8-9"], [12421, "12345-6-7+89"], [12270,
"12345-6-78+9"], [12257, "12345-6+7-89"], [12408, "12345-6+78-9"],
[12279, "12345-67-8+9"], [12277, "12345-67+8-9"], [12255,
"12345+6-7-89"], [12264, "12345+6-78-9"], [12395, "12345+67-8-9"],
[123450, "123456-7-8+9"], [123448, "123456-7+8-9"], [123446,
"123456+7-8-9"]]

Well this is very impressive as it solves the quiz but I lost him
there, I guess we have to look into the block applied to tmp.map
{|x| t = "1" + x.split(//).zip((2..9).to_a).join.delete(" ") ; [eval(t),t]}

okay let us take just one x, e.g.
x= tmp[32]
=> "- - +"
## To my great despair tmp[42] is not a very good example :(

Now we split x into single characters and zip the digits 2 to 9 into them
x.split(//).zip([*2..9])
=> [["-", 2], [" ", 3], [" ", 4], [" ", 5], ["-", 6], [" ", 7], [" ",
8], ["+", 9]]
and I think I understand what happened now, the rest is basic, add a 1
in the front flatten the array and delete all spaces, and you get the
expressions needed for the quiz.

I guess that the final sort.each is quite straight forward.

HTH

BTW I want to have my ball back James, or adjust my handicap please ;).

Cheers
Robert
 
R

Rick DeNatale

I agree, very clean and efficient. The three lines that put it all
together using #zip are what makes this work for me:

Digits.each_partition(Operators.length+1) do |digitspart|
OperatorPerms.each do |operatorsperm|
expression=digitspart.zip(operatorsperm).flatten.join
...

I think I would be feeling very happy if I'd submitted this solution :)

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.
 
M

Marcel Ward

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.

Indeed you may -- apologies, I thought I had been back through all the
posts but didn't go back as far as yours.

So I think you should also be feeling very happy :)

I do have one small piece of constructive criticism (if I may) since
you brought attention to your code:

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

Could also have been written:

found = val == goal
puts "*****************" if found
puts "#{eqn} = #{val}" if verbose || goal == val
puts "*****************" if found
found_in_a_row = found ? found_in_a_row + 1 : 0

For the same number of lines, I find the second much easier to follow
without having to remember any state for the second time around.
There does not seem to be any advantage in placing the asterisk lines
together. (?)

As another aside, I'm in two minds as to whether it's necessary to
provide such a complete set of parameters (and comments) for these
solutions. On the one hand it sets a good example to newcomers (and
it satisfies our quest for perfection) but on the other it does tend
to obscure some of the more interesting details. It's like you're
damned if you do and damned if you don't - the difficulty is finding
the right balance.

I think there's room for both kinds of posts but certainly the shorter
ones seem more appealing even if longer ones do use the same
underlying methods. I also like to work at making a solution more
generic for future purposes but I've come to the conclusion that
(unless the extra credits mention it) there's no point because I'm
only going to prejudice my solution.

In the real world I would go for the more generic code and proper
comments any day but for the purposes of the quiz I like to see
solutions that do just as much as is asked of them and ideally fit on
one page of my screen.
 
P

paul

Thanks for another fun Ruby Quiz. Here is my take on it, I did not
see other solutions that used String formatting to generate the
expressions.

class String
def unique_permutations
# modified to get unique permutations
# from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/139858
# which says it was inspired by a RubyQuiz! :)
return [self] if self.length < 2
perms = Hash.new

0.upto(self.length - 1) do |n|
#rest = self.split('')
rest = self.split(//u) # for UTF-8 encoded strings
picked = rest.delete_at(n)
rest.join.unique_permutations.each { |x| perms[picked + x] =
nil }
end

perms.keys
end
end

digits = ARGV[0]
ops = ARGV[1]
target = ARGV[2].to_i

# pad ops list with spaces to match the number of slots between the
digits
ops = ops + " " * (digits.size - ops.size - 1)

# build a format string with slots between the digits
digits = digits.split("").join("%s")


operator_perms = ops.unique_permutations
operator_perms.each do |p|
# build expression by inserting the ops into the format string,
# after converting spaces to empty strings
exp = digits % p.split("").map{|x|x.chomp(" ")}
val = eval(exp)
puts "*******************" if val==target
puts exp + " = " + val.to_s
puts "*******************" if val==target
end
puts
puts "%d possible equations tested" % operator_perms.size


Regards,
Paul
 
H

Harrison Reiser

May I have the temerity to point out that I posted basically the same
solution, which I posted two hours before Ken's.

Indeed! Well, isn't too coincidental after all for me to have used
the same mechanism. Great minds must think alike, no?

Harrison Reiser
 
J

James Edward Gray II

BTW I want to have my ball back James, or adjust my handicap
please ;).

I'm just glad you broke that code down for us. Now maybe I can read
it to look smart when I write up the summary. ;)

James Edward Gray II
 
H

Harrison Reiser

Thanks for another fun Ruby Quiz. Here is my take on it, I did not
see other solutions that used String formatting to generate the
expressions.

class String
def unique_permutations
# modified to get unique permutations
# fromhttp://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/139858
# which says it was inspired by a RubyQuiz! :)
return [self] if self.length < 2
perms = Hash.new

0.upto(self.length - 1) do |n|
#rest = self.split('')
rest = self.split(//u) # for UTF-8 encoded strings
picked = rest.delete_at(n)
rest.join.unique_permutations.each { |x| perms[picked + x] =
nil }
end

perms.keys
end
end

Clever use of hashes. I wrote a String#each_unique_permutation, but
didn't think that it could be used like this.
# pad ops list with spaces to match the number of slots between the
digits
ops = ops + " " * (digits.size - ops.size - 1)

# build a format string with slots between the digits
digits = digits.split("").join("%s")

operator_perms = ops.unique_permutations
operator_perms.each do |p|
# build expression by inserting the ops into the format string,
# after converting spaces to empty strings
exp = digits % p.split("").map{|x|x.chomp(" ")}

I like that you permute the ops string with extra spaces. Much
simpler than something like digits.each_ordered_partition.

Harrison Reiser
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,591
Members
45,102
Latest member
GregoryGri
Top