Here's my solution. It uses the A* algorithm and the priority queue stuff
from quiz #44. Btw, I'm new to ruby quiz, too. :)
Pablo
---
#!/usr/bin/ruby
INF = 1.0/0.0
LOG2 = Math.log(2)
# The priority queue is taken from Ruby Quiz #44 (by Levin Alexander).
class PriorityQueue
def initialize
@storage = Hash.new { |hash, key| hash[key] = [] }
end
def push(data, priority)
@storage[priority] << data
end
def next
return nil if @storage.empty?
key, val = *@storage.min
result = val.shift
@storage.delete(key) if val.empty?
return result
end
end
def solve(a, b)
return nil if a < 0 || b < 1
q = PriorityQueue.new
cost = Hash.new{INF}
parent = {}
q.push a, 0
cost[a] = 0
logb = Math.log(b)
while n = q.next
break if n == b
sub = []
sub << n*2
sub << n/2 if n%2 == 0
sub << n+2
sub.each do |s|
# Number of operations from a to s
c = cost[n] + 1
# Discard this path if we already have a better/equal one
next if cost[s] <= c
cost[s] = c
parent[s] = n
# h = estimated min. number of operations required from s to b
x = (s > 0) ? s : 1 # log(0) = error
# This computes the number of *2 or /2 operations needed
# to go from s to b
h = ((logb-Math.log(x))/LOG2).abs.floor
q.push s, c + h
end
end
# Build the path backwards
path, n = [b], b
while n = parent[n]
path << n
end
path.reverse
end
if __FILE__ == $0
if ARGV.length == 2
steps = solve(ARGV[0].to_i, ARGV[1].to_i)
puts "#{steps.inspect} (#{steps.length})"
else
puts "Usage: #{$0} <from> <to>"
end
end