Avoiding recursive call would imply iterating bits starting from
most significant, which is not easy to do efficiently.
Any saving would be dwarfed by the multiplications anyways.
[Feature #15233]
This commit is contained in:
Marc-Andre Lafortune 2020-12-04 01:57:40 -05:00 коммит произвёл Marc-André Lafortune
Родитель 3b5b309b7b
Коммит a83a51932d
2 изменённых файлов: 44 добавлений и 15 удалений

Просмотреть файл

@ -1233,26 +1233,49 @@ class Matrix
# # => 67 96
# # 48 99
#
def **(other)
case other
def **(exp)
case exp
when Integer
x = self
if other <= 0
x = self.inverse
return self.class.identity(self.column_count) if other == 0
other = -other
end
z = nil
loop do
z = z ? z * x : x if other[0] == 1
return z if (other >>= 1).zero?
x *= x
case
when exp == 0
_make_sure_it_is_invertible = inverse
self.class.identity(column_count)
when exp < 0
inverse.power_int(-exp)
else
power_int(exp)
end
when Numeric
v, d, v_inv = eigensystem
v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv
v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** exp}) * v_inv
else
raise ErrOperationNotDefined, ["**", self.class, other.class]
raise ErrOperationNotDefined, ["**", self.class, exp.class]
end
end
protected def power_int(exp)
# assumes `exp` is an Integer > 0
#
# Previous algorithm:
# build M**2, M**4 = (M**2)**2, M**8, ... and multiplying those you need
# e.g. M**0b1011 = M**11 = M * M**2 * M**8
# ^ ^
# (highlighted the 2 out of 5 multiplications involving `M * x`)
#
# Current algorithm has same number of multiplications but with lower exponents:
# M**11 = M * (M * M**4)**2
# ^ ^ ^
# (highlighted the 3 out of 5 multiplications involving `M * x`)
#
# This should be faster for all (non nil-potent) matrices.
case
when exp == 1
self
when exp.odd?
self * power_int(exp - 1)
else
sqrt = power_int(exp / 2)
sqrt * sqrt
end
end

Просмотреть файл

@ -448,6 +448,12 @@ class TestMatrix < Test::Unit::TestCase
assert_equal(Matrix[[67,96],[48,99]], Matrix[[7,6],[3,9]] ** 2)
assert_equal(Matrix.I(5), Matrix.I(5) ** -1)
assert_raise(Matrix::ErrOperationNotDefined) { Matrix.I(5) ** Object.new }
m = Matrix[[0,2],[1,0]]
exp = 0b11101000
assert_equal(Matrix.scalar(2, 1 << (exp/2)), m ** exp)
exp = 0b11101001
assert_equal(Matrix[[0, 2 << (exp/2)], [1 << (exp/2), 0]], m ** exp)
end
def test_det