Here it is, my solution. From mathematical and algorithmic point of view,
you won't find anything new. However:

 - One of the few times that class variables come in handy: I put the
   halve/double operations into Integer and then decided to make the do-all
   method Integer.solve_num_maze but the steps are done in
   Integer#handle_num_maze

 - Both optimizations are done in the method #enqueue

My permanent URL has header, license anouncement and large testsuite (orig
from swaits.com, unaccessible; not updated) included:
   http://members.chello.nl/k.vangelder/ruby/quiz/

The code itself:

class Integer
  def even?
    self[0] == 0
  end

  def odd?
    self[0] == 1
  end

  def halve
    self / 2  if self.even?
  end

  def double
    self * 2
  end

  # add inverse for add_two (we're doing DynProg)
  def sub2
    self - 2
  end

  Step = Struct.new(:dist, :next)

  def self.source; @@source; end
  def self.route; @@route; end
  def self.queue; @@queue; end

  def source; @@source; end
  def route; @@route; end
  def queue; @@queue; end

  def self.solve_num_maze(source, target)
    raise ArgumentError.new("Can't solve from >=0 to <0")  if target < 0 and source >= 0
    raise ArgumentError.new("Can't solve from >0 to 0")  if target <= 0 and source > 0
    @@source = source
    @@route = {}
    @@queue = []
    @@max = [(source + 2) * 2, target * 2].max
    # @@max = [source, target].max << 2  # and other attempts
    queue << target
    route[target] = Step.new(0, nil)
    while (job = queue.shift) != source
      job.handle_num_maze
    end

    result = [source]
    step = source
    while step != target
      step = route[step].next
      result << step
    end
    result
  end

  def enqueue(job)
    # optimization 1, do not go through pending searches when effectively done
    queue.clear  if job == source

    # optimization 2, do not look for solutions where it is not necessary
    queue << job  if job <= @@max
  end

  def handle_num_maze
    if route[double].nil? or route[self].dist + 1 < route[double].dist
      route[double] = Step.new(route[self].dist+1, self)
      enqueue double
    end
    # mind the extra check on existence of #halve
    if halve and (route[halve].nil? or route[self].dist + 1 < route[halve].dist)
      route[halve] = Step.new(route[self].dist+1, self)
      enqueue halve
    end
    if route[sub2].nil? or route[self].dist + 1 < route[sub2].dist
      route[sub2] = Step.new(route[self].dist+1, self)
      enqueue sub2
    end
  end
end

p Integer.solve_num_maze(ARGV[0].to_i, ARGV[1].to_i)