Ok, I had some free time. The enclosed solution still finds less
numbers than marcelo's elegant solution but it is slightly faster.

For 100_000_000_000_000_000_000_000

marcelo: 572 numbers, 1m28s
my enclosed solution: 550 numbers, 0m1.583s

Unless you want to find 550 reverse divisible numbers really fast
there is no real reason to use the enclosed version due to the
imperfect rules set. Anyway, here it is:


def rdn(limit = 1_000_000)
    rules = [
        lambda {|t, th, a, b|
            t.sub(/^(#{th}#{a}9*)#{b}/, "\\19#{b}")
        },
        lambda {|t, th, a, b|
            t.sub(/^(#{th}0*)/, '\\10')
        },
        lambda {|t, th, a, b|
            t.gsub(/#{b}(0*)#{a}/, "#{b}\\10#{a}")
        },
        lambda {|t, th, a, b|
            t.sub(/^(#{th})(#{th})$/, "\\1#{a}#{b}\\1")
        },
        lambda {|t, th, a, b|
            z  = th[/0+$/]
            th = th[0, th.size - z.size] if z
            t.sub(/^(#{th}#{z})(#{z}#{th})$/, "\\1#{a}#{b}#{a}#{b}\
\2")
        },
        lambda {|t, th, a, b|
            th ? ["#{a}#{b}0", th, th, "0#{a}#{b}"].join : t
        },
        lambda {|t, th, a, b|
            th ? ["#{a}#{b}", th, th, "#{a}#{b}"].join : t
        },
        lambda {|t, th, a, b| [t, t].join},
    ]

    half = {
        '1089' => lambda {|t| t[0, t.size / 2][/^(109*890*)*/]},
        '2178' => lambda {|t| t[0, t.size / 2][/^(219*780*)*/]},
    }

    digits = limit.to_s.size

    out = {1089 => 9801, 2178 => 8712}

    axioms = [
        ['1089'],
        ['2178']
    ]

    axioms.each do |nums|
        nums0    = nums[0]
        nums0pre, nums0post = nums0.scan(/../)
        makehalf = half[nums0]
        cache = Hash.new do |h, n|
            nh = makehalf.call(n)
            rules.each do |rule|
                n1  = rule.call(n, nh, nums0pre, nums0post)
                n1i = n1.to_i
                if n1 != n and !out.has_key?(n1i) and n1.size < digits
                    m1i = n1.reverse.to_i
                    if m1i < limit
                        nums << n1
                        if m1i % n1i == 0
                            out[n1i] = m1i
                            val = true
                        end
                    end
                end
            end
            h[n] = false
        end
        begin
            found = false
            nums.each {|n| found ||= cache[n]}
        end while found
    end
    return out
end


if __FILE__ == $0
    limit  = (ARGV[0] || 1_000_000).to_i
    nums = rdn(limit).sort_by {|a, b| b}
    nums.each do |n1i, m1i|
        puts "#{m1i} = #{n1i} * #{m1i / n1i}"
    end
    puts "Found #{nums.size} numbers"
end