Вот быстрая и пошлая реализация, которая поможет вам, пока не появится хорошая:
def mortanize(x, y)
xs, ys = [x, y].map do |n|
n.to_s(2)
end
nbits = [xs, ys].map(&:size).max
xs, ys = [xs, ys].map do |n|
('0' * (nbits - n.size) + n).chars
end
ys.zip(xs).join.to_i(2)
end
Как и следовало ожидать, это не скорость, Дэймон. На моем компьютере с MRI 1.8.7 он вычисляет около 35 000 16-битных результатов в секунду. Ваш вычисляет 68 000 16-битных результатов в секунду. Или посмотрите следующий алгоритм для 256 000 16-битных результатов в секунду.
Если вы готовы обменять немного памяти и время запуска на скорость, то:
def base_mortanize(x, y)
xs, ys = [x, y].map do |n|
n.to_s(2)
end
nbits = [xs, ys].map(&:size).max
xs, ys = [xs, ys].map do |n|
('0' * (nbits - n.size) + n).chars
end
ys.zip(xs).join.to_i(2)
end
MORTON_TABLE_X = 256.times.map do |x|
base_mortanize(x, 0)
end
MORTON_TABLE_Y = 256.times.map do |y|
base_mortanize(0, y)
end
def mortanize(x, y)
z = []
while (x > 0 || y > 0)
z << (MORTON_TABLE_X[x & 0xff] | MORTON_TABLE_Y[y & 0xff])
x >>= 8
y >>= 8
end
z.reverse.inject(0) do |result, word|
result << 16 | word
end
end
Это вычисляет 256 000 16-битных результатов в секунду.
В вашем ответе есть ошибка, если любой аргумент равен нулю. Вот одно из возможных исправлений. Сначала определите эту функцию:
def bit_size(x)
return 1 if x == 0
Math.log2(x).floor + 1
end
А затем, внутри interleave
, заменить:
z, bl, ybl = 0, (Math.log2(self)).floor + 1, (Math.log2(y)).floor + 1
с:
z = 0
bl = bit_size(x)
ybl = bit_size(y)
Вот пример теста rspec, который я использовал:
describe "mortanize" do
it "should interleave integers" do
mortanize(0, 0).should eql 0
mortanize(0, 1).should eql 2
mortanize(1, 0).should eql 1
mortanize(0xf, 0x3).should eql 0x5f
mortanize(0x3, 0xf).should eql 0xaf
mortanize(0xf, 0x0).should eql 0x55
mortanize(0x0, 0xf).should eql 0xaa
mortanize(0x3, 0xc).should eql 0xa5
mortanize(0xf, 0xf).should eql 0xff
mortanize(0x1234, 0x4321).should eql 0x210e0d12
end
end