我需要生成一个这样的序列:
[1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81]
(本例中为17个数字)
算法是:
[1, (previous + 2), (previous + 2), (previous + 2), (previous + 2), (previous + 4), (previous + 4), (previous + 4), (previous + 4) ...
因此,对于4个第一项,它是+2,然后是接下来4的+4,接下来是4的+6。每四个项增加2。
我能够在Ruby中做一个快速和hacky版本:
def sequence
incr = 0
(0..16).each.inject([]) do |acc, counter|
acc << (acc.last || 1) + incr
incr += 2 if counter.modulo(4) == 0
acc
end
end
但是我在Elixir做同样的问题 - 结果却是超级跛脚。像这样:
def sequence do
{ sequence, _ } =
0..16
|> Enum.reduce({[], 0}, fn(counter, {result, incr}) ->
last = List.last(result)
if last do
result = result ++ [last + incr]
else
result = [1]
end
if rem(counter, 4) == 0 do
incr = incr + 2
end
{result, incr}
end)
sequence
end
显然我不应该在这里强制思考,但对于这个问题我不能:D 我也确信有一种管道更加原子化的方法。
如何以Elixir的方式解决这个问题?
答案 0 :(得分:5)
我用{[1], 0}
启动累加器,以删除函数体中的特殊情况。通常不建议使用List.last
和++
,因为它们效率低(O(n)
)。 Elixir中的惯用方法是反向构建列表并在结尾处反转列表。这意味着现在可以通过匹配列表头部的模式处理您的List.last
逻辑,这很便宜。您还应该收到警告,在if中分配incr
。惯用的方法是做incr = if ..., do: incr + 2, else: incr
。
以下是我如何写这个:
(0..16)
|> Enum.reduce({[1], 0}, fn counter, {[h | _] = result, incr} ->
incr = if rem(counter, 4) == 0, do: incr + 2, else: incr
{[h + incr | result], incr}
end)
|> elem(0)
|> Enum.reverse
|> IO.inspect
输出:
[1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81, 91]
答案 1 :(得分:2)
在这种情况下,你也可以使用一个等式:
f(n) = 1 + 2*(k+1)*(2k+j), where k = div(n/4), j = rem(n/4)
在elixir中会是这样的:
Enum.map((0..16), fn n ->
k = div(n,4)
1 + 2*(k+1)*(2*k + rem(n,4))
end)
# => [1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81]
答案 2 :(得分:0)
这是一个有趣的问题:) https://stackoverflow.com/a/46601289/24105看起来是最好的解决方案。如果你能得到某个公式,那总是最快的。但是,在这种情况下,我想看看是否还有其他解决方案。这是我的看法:
defmodule S do
#1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49, 57, 65, 73, 81
#So it's +2 for 4 first items, then +4 for next 4, then +6 for next 4. Increment is increased by 2 each four items.
def gen1(n) do
0..(n-2)
|> Enum.reduce([1], fn x, [prev | _] = acc ->
incr = ((div(x, 4) + 1) * 2)
[prev + incr | acc]
end)
|> Enum.reverse
end
def gen2(n) do
(n - 1)
|> incr_series
|> Enum.reduce([1], fn incr, [prev | _] = acc ->
[prev + incr | acc]
end)
|> Enum.reverse
end
defp incr_series(n) do
1..(div(n, 4) + 1)
|> Enum.flat_map(fn x -> List.duplicate(x*2, 4) end)
|> Enum.take(n)
end
def generate(n, algorithm), do: apply(__MODULE__, algorithm, [n])
end
ExUnit.start
defmodule AccumSeqTest do
use ExUnit.Case, async: true
for fun <- [:gen1, :gen2] do
describe to_string(fun) do
test "first 4 should increment by 2" do
assert S.generate(3, unquote(fun)) == [1, 3, 5]
assert S.generate(5, unquote(fun)) == [1, 3, 5, 7, 9]
end
test "second 4 should increment by 4" do
assert S.generate(6, unquote(fun)) == [1, 3, 5, 7, 9, 13]
assert S.generate(9, unquote(fun)) == [1, 3, 5, 7, 9, 13, 17, 21, 25]
end
test "third 4 should increment by 4" do
assert S.generate(10, unquote(fun)) == [1, 3, 5, 7, 9, 13, 17, 21, 25, 31]
assert S.generate(13, unquote(fun)) == [1, 3, 5, 7, 9, 13, 17, 21, 25, 31, 37, 43, 49]
end
end
end
end