2017-10-03 7 views
1

私はこのようなシーケンスを生成する必要があります
[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、次の場合は+6、4の場合は2ずつ増加します。

私はRubyで迅速かつハックバージョンを行うことができました:

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 

しかし、私はエリクサーで同じことをやって問題を抱えています - それは、超ラメが判明します。このように:もちろん、私は命令的に、ここで考えてはいけませんが、私はできません。この問題のために

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 私もパイプがはるかに原子的なアプローチがあると確信しています。

この問題はエリクシールの方法でどのように解決できますか?

答えて

5

私は{[1], 0}でアキュムレータを起動して、関数の本体にある特別なケースを削除します。 List.lastおよび++は、非効率的であるため一般的には推奨されません(O(n))。エリクサーの慣用的なやり方は、リストを逆に作成し、最後にリストを逆にすることです。これは、あなたの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] 
+0

おかげさまでヒープ! ':lists.reverse'を' Enum.reverse'に使う利点もありますか? – konnigun

+1

実際はありません。 ':lists.reverse'は、' Enum.reverse'が最初に型チェックを行い、リストを見つけたら ':lists.reverse'に委譲するので、少し早いかもしれません。パフォーマンスの差は重要ではありません。私は 'Enum.reverse'に変更します。 – Dogbert

2

f(n) = 1 + 2*(k+1)*(2k+j), where k = div(n/4), j = rem(n/4) 

た万能薬になり次のようなものにしてください:

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] 
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 
関連する問題