优化后的 Str Rep |
|
function log2(n)
local _n = 2
local x = 1
if (_n < n) then
repeat
x = x + 1
_n = _n + _n
until (_n >= n)
elseif (_n > n) then
if (n == 1) then
return 0
else
return nil
end
end
if (_n > n) then
return x-1
else
return x
end
end
function get_bits(n)
local bits = {}
local rest = n
repeat
local major_bit = log2(rest)
rest = rest - 2^major_bit
bits[major_bit] = 1
if (bits.count == nil) then
bits.count = major_bit
end
until (rest == 0)
return bits
end
function fast_strrep(str, times)
local bits = get_bits(times)
local strs = {[0] = str}
local count = bits.count
for i = 1, count do
strs[i] = strs[i-1] .. strs[i-1]
end
local result = ''
for i = 0, count do
if (bits[i]) then
result = result .. strs[i]
end
end
return result
end
for numreps = 1024, 30*1024*1024, 1024*64 do
a = nil
b = nil
collectgarbage()
start = clock()
a = fast_strrep("a", numreps)
print("L:"..numreps.." "..(clock() - start))
start = clock()
b = strrep("a", numreps)
print("C:"..numreps.." "..(clock() - start))
if (a~=b) then
print("the algorithm is wrong!")
else
print("ok")
end
flush(_STDOUT)
end
a .. b .. c 优化为单个操作的事实,并且还避免了创建临时向量。我认为你会发现它比你的算法快大约两倍(而且代码更短)。它也可能作为一个例子,说明如何在不造成太多痛苦的情况下进行类似位操作的操作。 -- RiciLake
-- Suppose that x = b[n]*2^n + b[n-1]*2^(n-1) + ... + b[0]*2^0
-- (where every b[i] is either 0 or 1)
-- This is exactly equivalent to:
-- b[0] + 2 * (b[1] + 2 * (b[2] + (... + b[n])))
-- So we've effectively eliminated all the multiplications, replacing them with doubling.
-- Now, x * y (for any y) can be calculated by distributing multiplication over the
-- above, which effectively replaces every b[i] with b[i] * y. However, every b[i]
-- is either 0 or 1, so the product is either 0 or y.
-- Now, if k is an integer and str1 and str2 are strings, and we write:
-- str1 + str2 for the concatenation of str1 and str2
-- k * str1 for "k copies of str1 concatenated"
-- we can see that we have + and * are "just like" integer arithmetic in the sense that
-- + and * are commutative and associative, and * distributes over +. So the equivalence
-- continues to work, except that every term must be either "" (for 0) or y (the string).
-- All that is left is to compute the expression from the inside out: each step is
-- either 2 * r or y + 2 * r, where r is the cumulated value and y is the original string.
-- In string terms, we can write these as result .. result (2 * r) and
-- result .. result .. str (2 * r + y)
-- We could use the same idea to compute integer exponents in the minimum number of
-- multiplications, using * and ^ instead of + and * (which is where this algorithm
-- comes from.)
-- This makes use of the fact that Lua optimises a .. b .. c into a single concatenation.
-- With a bit more work, we could use any base we wanted to, not just base 2. But it would
-- require more options in the if statement.
function another_strrep(str, times)
local result = ""
local high_bit = 1
while high_bit < times do high_bit = high_bit * 2 end
-- at this point, high_bit is the largest (integral) power of 2 smaller than times
-- (unless times < 1 in which case high_bit is 1)
-- The computation of highbit could be:
-- local high_bit = 2 ^ floor(log(times) / log(2))
-- which is probably faster but requires the math library
-- we are now going to work through times, bit by bit, making use of the above formula:
while high_bit >= 1 do
if high_bit <= times then -- the bit is 1 if times is >= high_bit
times = times - high_bit -- we "turn it off" for the next iteration
result = result .. result .. str -- and the next step is 2 * r + y
else -- the bit is 0
result = result .. result -- so the next step is 2 * r
end
high_bit = high_bit / 2 -- Now go for the next bit
end
return result
end
luiz/rici = 1.41 (rici is 1.41 times faster than luiz)
c/luiz = 2.98 (luiz is 2.98 times faster than c)
c/rici = 4.19 (rici is 4.19 times faster than c)
"" .. "" .. str。
%state 是一个标准技巧,用于解决 Lua 4.0 没有真正的闭包的问题。
join 函数,它会延迟编译子例程来执行相同类型的指数分解问题;即使它必须组合和编译函数,它最终比朴素的 join 快得多。当然,这些函数必须被记忆才能利用这一点。我会尝试发布那个函数。 -- RiciLake
do
local concats = {
["0"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a end,
["1"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b end,
["2"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b end,
["3"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b end,
["4"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b end,
["5"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b .. b end,
["6"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b .. b .. b end,
["7"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b .. b .. b .. b end,
["8"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b .. b .. b .. b .. b end,
["9"] = function(a, b) return a .. a .. a .. a .. a .. a .. a .. a .. a .. a
.. b .. b .. b .. b .. b .. b .. b .. b .. b end,
}
function decimal_strrep(str, times)
local state = {r = "" }
local concats = %concats
times = tostring(times)
if strfind(times, "^[0-9]+$") then
gsub(times, "(.)",
function(digit)
%state.r = %concats[digit](%state.r, %str)
end)
end
return state.r
end
end