Counting Toothpicks (#111)

Well, here’s my solution. (Parked at Loopia)

I leaned heavily on #prime_division, so my solution can be a little
slow for numbers larger than 10^5. I’m not confident that I have a
globally optimal algorithm.

This quiz was quite hard for me, and I spent an unseemly amount of time
on it.

Krishna


require ‘mathn’

Combines 2s and 3s into 4s and 6s, which are more toothpick-efficient.

Takes a ‘prime division’ argument such as [[2,3], [3,2]], which it

would return as [[3,1], [4,1], [6,1]]. The primes must be in order.

def merge_primes(pd)
if pd[0] && pd[0][0] == 2
if pd[0][1] > 1
pd << [4, (pd[0][1] / 2).to_i] # for some bizarre reason i have
to call to_i here to avoid a fraction
pd[0][1] = pd[0][1] % 2
end
if pd[1] && pd[1][0] == 3 && pd[0][1] == 1
pd << [6, 1]
pd[1][1] -= 1
pd[0][1] -= 1
end
end
pd.reject{|e| e[1] == 0}
end

Expects an array of ‘prime division’-like objects

def to_toothpicks(ar)
ar.map { |pd|
pd.map {|e| Array.new(e[1]){|i| “|” * e[0]}}.join(“x”)
}.join “+”
end

Expects an array of ‘prime division’-like objects

def cost(ar)
c = 0
for pd in ar
for i, n in pd
c += in + n2
end
end
c -= 2
end

Returns an array of ‘prime division’-like objects

def best_toothpicks(i)
options = []
rem = 0

if i < 8 || i == 11
options << [[[i, 1]]]
else
while true
ar = i.prime_division
option = [merge_primes(ar)]
option += best_toothpicks(rem) if rem > 0
options << option

  # this is something i'm not happy about. larger numbers (7)

improve performance,
# but i’m afraid smaller ones (3) may find better solutions
if ar.detect {|e| e.first > 5 }
i -= 1
rem += 1
else
break
end
end
end
return options.min {|a, b| cost(a) <=> cost(b) }
end

i = ARGV[0].to_i
puts to_toothpicks(best_toothpicks(i))


It’s my first time on ruby-quiz. So here’s my solution. It’s some kind
of dynamic-programming:

Number to calculate with toothpicks

class ToothNumber
attr_reader :value, :num, :pic
def initialize value, num=value, pic=("|"*num)
@value, @num, @pic = value, num, pic
end

def + x; operation(:+, 2, "+", x); end

# TODO: uncomment line for use of '-' ############
#def - x; operation(:-, 1, "-", x); end

def * x; operation(:*, 2, "x", x); end

def <=> x; @num <=> x.num; end

def to_s; "#{@pic} = #{@value} (#{@num} Toothpicks)"; end

private
# create new ToothNumber using an operation
def operation meth, n_operator_sticks, operator, x

ToothNumber.new @value.send(meth, x.value),
@num + x.num + n_operator_sticks,
@pic + operator + x.pic
end
end

contains minimal multiplication-only toothpick for each number

$tooths = Hash.new {|h,n| h[n] = tooth_mul n}
$tooths_add = Hash.new {|h,n| h[n] = toothpick n}

should return the minimal toothpick-number

should only use multiplication

def tooth_mul n
ways = [ToothNumber.new(n)] +
(2…(Math.sqrt(n).to_i)).map{|i|
n % i == 0 ? ($tooths[i] * $tooths[n/i]) : nil
}.compact
ways.min
end

returns minimal toothpick-number with multiplication and addition

def toothpick n
ways = [$tooths[n]] +
# TODO: uncomment the following line for use of ‘-’
# I do not know, if n = (x+i) - i for i \in {1,2,…,n} is ok
# (1…n).map{|i| $tooths[n+i] - $tooths[i]} +
(1…(n/2)).map{|i| $tooths[n-i] + $tooths_add[i] }
ways.min
end

for i in 1…ARGV[0].to_i
puts $tooths_add[i]
end

Well here is my solution. I thought it was pretty good until I saw yours
Frank :slight_smile:
It:
(1) produces optimal solutions
(2) returns minimal plus counts in tie break situations (there are no
integers that need 3 pluses before 20000 btw)

Exactly like Franks it is recursive on multiplication and bi-partitions.
Unlike franks it does not do clever things with class operators and hash
defaults that would reduce the LOC by 50%. Ah well.

$ ruby toothpick.rb 510

510: (5x6x17) -> 32

Will return the toothpick expression with the minimum

number of toothpicks. In the case of tie-breaks, it will return

the number with the fewest ‘+’ signs.

toothpick expressions are stored like:

[[1], [3 ,4], [5, 6]] ~ 1 + 34 + 56

class Integer
def divisors
(2…(self-1)).select do |i|
self.divmod(i)[1] == 0
end
end
end

class Toothpicks

contains the factorizations of integers with the minimum toothpick

counts
FACTORIZATIONS = [nil, [1], [2], [3], [4], [5], [6], [7]]

contains the best toothpick expression for each integer

SIMPLIFICATIONS = [nil, [1], [2], [3], [4], [5], [6], [7]]

contains the best toothpick expression’s toothpick count

SIMPLIFICATION_COUNTS = [nil, 1, 2, 3, 4, 5, 6, 7]

contains the best toothpick expressions + count

PLUS_COUNTS = [nil, 0, 0, 0, 0, 0, 0, 0]

def self.go(int, print=false)
r = nil
1.upto(int) do |i|
r = simplify(i)
puts "#{i}: "+display® if print
end
r
end

counts toothpicks in an array

def self.count(array)
array.flatten.inject{|sum, el| sum+el} + 2*array.flatten.length - 2
end

just pretty prints the toothpick expression

def self.display(array)
str = “(”
array.each do |el|
if el.is_a? Array
str << el.join(“x”)
elsif el.is_a? Integer
str << el.to_s
end
str << " + "
end
str[0…(str.length-4)] << “) -> #{count(array)}”
end

factorize an integer using the fewest toothpicks possible.

Recursive on multiplication.

def self.factorize(int)
if FACTORIZATIONS[int]
result = FACTORIZATIONS[int]
else
best = [int]
best_value = count(best)
sqrt = Math::sqrt(int.to_f).to_i
int.divisors.select{|d| d <= sqrt}.each do |div|
current = [factorize(div), factorize(int/div)].flatten
value = count(current)
if value < best_value
best = current
best_value = value
end
end
FACTORIZATIONS[int] = best
end
end

simplify an integer into a sum of factorized integers using

the fewest toothpicks possible.

(assumes that all simplifications less that int have already been

done)

Recursive on bi-partition.

def self.simplify(int)
factorization = factorize(int)
best = 0
best_value = count(factorization)
best_plus_count = 0
# iterate over all possible bi-partitions of int, and save the best
1.upto(int/2) do |i|
value = SIMPLIFICATION_COUNTS[i] + SIMPLIFICATION_COUNTS[-i] + 2
if value < best_value
best = i
best_value = value
best_plus_count = PLUS_COUNTS[i] + PLUS_COUNTS[-i] + 1
elsif value == best_value
plus_count = PLUS_COUNTS[i] + PLUS_COUNTS[-i] + 1
if plus_count < best_plus_count
best = i
best_value = value
best_plus_count = plus_count
end
end
end
SIMPLIFICATION_COUNTS[int] = best_value
PLUS_COUNTS[int] = best_plus_count
if best == 0
SIMPLIFICATIONS[int] = [factorization]
else
SIMPLIFICATIONS[int] = SIMPLIFICATIONS[best] +
SIMPLIFICATIONS[-best]
end
end
end

if ARGV[0] == “test”
require ‘test/unit’
class TestToothpick < Test::Unit::TestCase
def test_factorize
assert_equal [3, 4], Toothpicks.factorize(12)
assert_equal [13], Toothpicks.factorize(13)
assert_equal [2, 7], Toothpicks.factorize(14)
end

def test_count
  assert_equal 12, Toothpicks.count( [[3,4], [1]] )
  assert_equal 11, Toothpicks.count( [[3,6]] )
  assert_equal 14, Toothpicks.count( [[1], [3,6]] )
end

def test_simplify_through_go
  assert_equal [[1]], Toothpicks.go(1)
  assert_equal [[3]], Toothpicks.go(3)
  assert_equal [[3, 3]], Toothpicks.go(9)
  assert_equal [[1], [3, 6]], Toothpicks.go(19)
end

end
else
r = Toothpicks.go(ARGV[0].to_i)
puts "#{ARGV[0].to_i}: "+Toothpicks.display®
end

On 26/01/07, Ruby Q. [email protected] wrote:

This weeks quiz is to write a program that takes a single command-line argument
which will be a positive integer. Your code should build a toothpick expression
to calculate the number using as few toothpicks as possible. For example:

    $ruby toothpick.rb 9
    |||x||| = 9 (8 toothpicks)

Don’t forget to count those operators!

Here is my solution. It is an iterative solution using two caches:
one for “multipicable” toothpick strings (i.e. those that do not
contain any addition signs) and another for “summable” toothpick
strings.

I take the approach that you can multiply as many previous
multiplicable strings together and then add a summable string, knowing
that the result will be valid.

I did start off by preferring shorter toothpick strings (as well as
less toothpicks) but after seeing the talk here decided that
minimising the number of operators (either + or x) is preferable
(again, for equal number of toothpicks). It turns out this should
favour multiplication over addition anyway.

Arghh, now my favourite coffee shop will have closed for the evening :wink:

Thanks James & Gavin and to other participants for the helpful example
expressions.

Marcel

Marcel W. <wardies ^a-t^ gmaildotcom>

Sunday, 28 January 2007

Solution for Ruby Q. number 111 - Counting Toothpicks

class Toothpicks
def initialize
# Lookups are value indexed arrays that maps to triplets
# of elements representing:
# 1. The shortest toothpick string form of the value
# 2. The number of toothpicks used
# 3. The number of operators used, i.e. multiply or add
@lookup_multiplicable = []
@lookup_summable = []
@max_cached_value = 0
end

def cache_next()
target = @max_cached_value + 1
# Find the best multiplicable tootpick expression by trying to
# multiply together two existing numbers from the multiplicable
# cache (we only need to search from 2…sqrt(target))
best_multiplicable = [one() * target, target, 0]
tpick_op, price_op = multiply()
x = 2
while x**2 <= target
y,remainder = target.divmod(x)
if remainder == 0
tpick_x, price_x, ops_x = @lookup_multiplicable[x]
tpick_y, price_y, ops_y = @lookup_multiplicable[y]
price = price_x + price_op + price_y
if (price < best_multiplicable[1]) ||
(price == best_multiplicable[1] &&
ops_x + ops_y + 1 < best_multiplicable[2])
best_multiplicable = [tpick_x + tpick_op + tpick_y, price,
ops_x + ops_y + 1]
end
end
x += 1
end

best_summable = best_multiplicable.dup
# Now try summing up two existing, cached values to see if this
# results in a shorter toothpick sum than the multiplicable one.
tpick_op, price_op = sum()
x = 1
y = target - x
while x <= y
  tpick_x, price_x, ops_x = @lookup_summable[x]
  tpick_y, price_y, ops_y = @lookup_summable[y]
  price = price_x + price_op + price_y
  if (price < best_summable[1]) ||
      (price == best_summable[1] &&
        ops_x + ops_y + 1 < best_summable[2])
    best_summable =[tpick_y + tpick_op + tpick_x, price,
      ops_x + ops_y + 1]
  end
  x += 1
  y -= 1
end
@max_cached_value += 1
@lookup_multiplicable[target] = best_multiplicable
@lookup_summable[target] = best_summable

end

def one()
“|”
end

def multiply()
[“x”, 2]
end

def sum()
[“+”, 2]
end

def smallest_summable(value)
# Cache any missing values
@max_cached_value.upto(value - 1) {cache_next()}
@lookup_summable[value].dup
end
end

def show_toothpicks(start, finish=start)
tp = Toothpicks.new()
start.upto(finish) do
|x|
toothpick_calc, cost = tp.smallest_summable(x)
puts “#{x}: #{toothpick_calc} (#{cost})”
end
end

case ARGV.size
when 1
show_toothpicks(ARGV[0].to_i)
when 2
show_toothpicks(ARGV[0].to_i, ARGV[1].to_i)
else
puts “Usage: ruby toothpick.rb -or- ”
end

On 1/28/07, Sander L. [email protected] wrote:

pos_mul = k.divisors.map{|d| h[d] + 'x ’ + h[k/d] }

Is there voting in this thing? If so, this gets my vote. I’m going
back to school.

With a bit less ugliness :

#!/usr/bin/env ruby

class String
def toothpick_count
count(’|-’) + 2count(’+’)
end

def toothpick_value
if count(’|’).zero?
0
else
eval(gsub(/(+|*|-|\Z)/, ‘)\1’).gsub(/(\A|+|*|-)|/,
‘\1(1’).gsub(/|/, ‘+1’))
end
end

def toothpick_information
“#{ gsub /*/, ‘x’ } = #{ toothpick_value } (#{ toothpick_count }
toothpick#{ ‘s’ unless toothpick_count == 1 })”
end
end

class Array
def add_in operation
1.upto(length - 1) do |i|
1.upto(i) do |j|
position = i.send(operation, j)
if (1…length).include? position
combination = “#{ self[i] }#{ operation }#{ self[j] }”
self[position] = combination if combination.toothpick_count
< self[position].toothpick_count
end
end
end
end
end

results = Array.new(ARGV.first.to_i + 1) { |i| ‘|’ * i }
results.add_in :*
results.add_in :+
puts results.last.toothpick_information

With a bit less ugliness :

#!/usr/bin/env ruby

class String
def toothpick_count
count(’|-’) + 2count(’+’)
end

def toothpick_value
if count(’|’).zero?
0
else
eval(gsub(/(+|*|-|\Z)/, ‘)\1’).gsub(/(\A|+|*|-)|/,
‘\1(1’).gsub(/|/, ‘+1’))
end
end

def toothpick_information
“#{ gsub /*/, ‘x’ } = #{ toothpick_value } (#{ toothpick_count }
toothpick#{ ‘s’ unless toothpick_count == 1 })”
end
end

class Array
def add_in operation
1.upto(length - 1) do |i|
1.upto(i) do |j|
position = i.send(operation, j)
if (1…length).include? position
combination = “#{ self[i] }#{ operation }#{ self[j] }”
self[position] = combination if combination.toothpick_count
< self[position].toothpick_count
end
end
end
end
end

results = Array.new(ARGV.first.to_i + 1) { |i| ‘|’ * i }
results.add_in :*
results.add_in :+
puts results.last.toothpick_information

A bit slow, but it should produce optimal output :

#!/usr/bin/env ruby

class String
def toothpick_count
count(’|+-’) + count(’+’)
end
end

class Array
def add_in operation
each_with_index do |expression, i|
(1…i).each do |j|
position = i.send(operation, j)
if (1…length).include? position
combination = “#{ expression }#{ operation }#{ self[j] }”
self[position] = combination if self[position] and
combination.toothpick_count < self[position].toothpick_count
end
end unless i.zero?
end
end
end

def output number, expression
puts “#{ expression.gsub /*/, ‘x’ } = #{ number }
(#{ expression.toothpick_count } toothpick#{ ‘s’ unless
expression.toothpick_count == 1 })”
end

result_wanted = ARGV.first.to_i

results = (0…result_wanted).map { |i| ‘|’ * i }
results.add_in :*
results.add_in :+

output result_wanted, results[result_wanted]

Here is my solution…works in most case, fails in some (like 34)

#!/usr/bin/ruby

Written by Andrey F.

def factor(n)
factors = []

    if (n < 8)
            factors.push(n)
            return factors
    end

    itr = 4
    itr = (n / 4).to_i if (n < 25)
    while (itr < n - (n / 2) + 1)
            if (n % (itr) == 0)
                    factors.concat(factor(itr))
                    factors.concat(factor(n / itr))
            else
                    itr = itr + 1
                    next
            end

            itr = itr + 1
            prod = 1
            for num in factors
                    prod = prod * num
            end

            return factors if (prod == n)
    end

    factors.push(n) if (factors.length == 0) # Primes

    return factors

end

def count(picks)
cnt = 0
strs = picks.split(‘x’)
for str in strs
cnt += 2
cnt += str.length
cnt += 1 if (str =~ /+/)
end

    return cnt - 2

end

def minPicks(n)
if (n <= 8)
return ‘|’ * n
else
factors = factor(n)
str = ‘’
if ((factors.length == 1 && factors[0] == n && n > 11)
|| n == 34)
len = n
itr = 1
while (8 < n - itr)
try = minPicks(n - itr) + ‘+’ + (’|’ *
itr)
itr += 1
if (len > (tmp = count(try)))
len = tmp
store = try
end
end

                    return store
            end

            for fac in factors
                    if (fac == n && n <= 11) # Primes <= 11
                            return '|' * n
                    else
                            str = str + minPicks(fac) + 'x'
                    end
            end

            str = str.gsub(/x$/, '')
            return str
    end

end

n = $*[0].to_i
picks = minPicks(n)

print n.to_s + “: " + picks.to_s + " (” + count(picks).to_s + "
toothpicks)\n"

On 1/29/07, George O. [email protected] wrote:

  @d ||= (2..Math.sqrt(self)).select{|i| self % i == 0 }

pos_plus = (k/2…k).map{|p| best_mul[p] + '+ ’ + h[k-p] }

The speedup here would be negligable, but hey:

pos_plus = (1..k/2).map{|p| best_mul[p] + '+ ' + h[k-p] }

I get a stack overflow w/ this.

h[k] = (pos_plus << best_mul[k]).sort_by{|tp|tp.length}.first
}.merge(1=>‘|’)

puts best_plus[ARGV[0].to_i].gsub(’ ‘,’').sub(/^$/,‘Hug a Tree’)

Sweet solution, though. :slight_smile:

I think so too, though I did find that the toothpicks weren’t being
counted correctly because it counted characters in each expression,
but not the extra toothpicks for + and x. So 11 was getting |||x|||+||
instead of |||||||||||.

This helped:

#in best_plus
h[k] = (pos_plus << best_mul[k]).sort_by{|tp|tp.length +
tp.split(/[x+]/).length - 1}.first

On 1/29/07, Sander L. [email protected] wrote:

Here is my solution, a simple brute force + cache.
Parked at Loopia

class Fixnum
def divisors
@d ||= (2…self/2).select{|i| self % i == 0 }

  You've probably already realized this, but this would be a lot 

faster:

  @d ||= (2..Math.sqrt(self)).select{|i| self % i == 0 }

end
end

best_mul = Hash.new{|h,k|
pos_mul = k.divisors.map{|d| h[d] + 'x ’ + h[k/d] }
h[k] = (pos_mul << ‘|’*k).sort_by{|tp|tp.length}.first
}

best_plus = Hash.new{|h,k|
pos_plus = (k/2…k).map{|p| best_mul[p] + '+ ’ + h[k-p] }

The speedup here would be negligable, but hey:

pos_plus = (1..k/2).map{|p| best_mul[p] + '+ ' + h[k-p] }

h[k] = (pos_plus << best_mul[k]).sort_by{|tp|tp.length}.first
}.merge(1=>‘|’)

puts best_plus[ARGV[0].to_i].gsub(’ ‘,’').sub(/^$/,‘Hug a Tree’)

Sweet solution, though. :slight_smile:

On 28/01/07, Frank F. [email protected] wrote:

warranty - are:
|||x|||x||||x||||||x|||||||+||||x||||+| = 1529 (46 Toothpicks)

Anyone know which the first one to need 3 is?
Not yet :slight_smile:

Well, after over 13 hours of CPU time (2GHz laptop)…

… on the 3rd revision of my code to ensure that it’s only using
170Mb of memory well into the 700_000s range (my initial version’s
“|” * 700_000 is NOT the most efficient calculation at this stage and
what results is certainly not going to be of any use!)…

… I am pleased (and relieved) to announce the first and (I think)
the lowest toothpick calculation with 3 plusses… :slight_smile:

775437:
|||x||||x||||x||||x||||x||||x||||||x||||||x|||||||+||||x||||x||||x||||x|||||+|||x||||+|
(103)

I sense some kind of exponential increase so I’m not going to even
think about trying to attempt finding 4 plusses.

On 1/30/07, David C. [email protected] wrote:

On 1/29/07, George O. [email protected] wrote:

On 1/29/07, Sander L. [email protected] wrote:
The speedup here would be negligable, but hey:

pos_plus = (1..k/2).map{|p| best_mul[p] + '+ ' + h[k-p] }

I get a stack overflow w/ this.

Darn, it does too. Calling best_mul[] on the larger addend (like
Sander’s original algo) helps:

    pos_plus = (1..k/2).map{|p| best_mul[k-p] + '+ ' + h[p] }

It wasn’t so obvious to me that it’s still correct that way, actually,
but I’m convinced it is.

I think so too, though I did find that the toothpicks weren’t being
counted correctly because it counted characters in each expression,
but not the extra toothpicks for + and x. So 11 was getting |||x|||+||
instead of |||||||||||.

Hmm, I didn’t get that behavior.

Note that he adds '+ ’ (plus-space) and 'x ’ (x-space) in the strings,
so I believe the string length does actually represent the toothpick
count correctly. Perhaps the spaces somehow disappeared when you
copied it?

“George O.” [email protected] writes:

class Fixnum
def divisors
@d ||= (2…self/2).select{|i| self % i == 0 }

 You've probably already realized this, but this would be a lot faster:

 @d ||= (2..Math.sqrt(self)).select{|i| self % i == 0 }

end
end

You reminded me of
ArticleS.UncleBob.PerformanceTuning