# Fibonacci ```elixir Mix.install([ {:benchee, "~> 1.3"}, {:kino_explorer, "~> 0.1.25"} ]) ``` ## Fibonacci hat változatban A Fibonacci-számok jól ismert matematikai definíciója: $F_0 = 0$
$F_1 = 1$
$F_i = F_{i-2} + F_{i-1}$, ha $i > 1$ Naív rekurzív megoldásunk ezt követi. Az $i$-edik Fibonacci-szám meghatározása elágazó rekurzióval nagyon rossz hatékonyságú, mert a két elágazó ágat minden egyes rekurzív lépésben újra meg újra teljesen be kell járni, azaz az $i$-ediknél kisebb Fibonacci-számokat újra és újra ki kell számolni, ráadásul a részeredményeket az egyre mélyülő veremben kell tárolni. ```elixir defmodule Fib do # Tree recursion # O(2^n) futási idő, O(2^n) tárhely @spec fib(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib(0), do: 0 def fib(1), do: 1 def fib(i), do: fib(i-1) + fib(i-2) end Fib.fib(23) #|> IO.inspect() ``` ``` 28657 ``` ```elixir defmodule FibM do # Memoization (top down) – dinamikus programozás # O(n) futási idő, O(n) tárhely @spec fib_mem(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_mem(i), do: fib_m(i, %{0 => 0, 1 => 1}) |> elem(0) @type mem() :: %{index :: integer() => value :: integer()} @spec fib_m(i :: integer(), mem :: mem()) :: {n :: integer(), uj_mem :: mem()} # n az i-edik Fibonacci-szám def fib_m(i, mem) do case mem[i] do # case nem váltható ki mintaillesztéssel nil -> {prev, memp} = fib_m(i-2, mem) {curr, memc} = fib_m(i-1, memp) val = prev + curr {val, Map.put(memc, i, prev+curr)} val -> {val, mem} end end end FibM.fib_mem(63) #|> IO.inspect() ``` ``` 6557470319842 ``` ```elixir defmodule FibT do # Tabulation (bottom-up) – dinamikus programozás # O(n) futási idő, O(n) tárhely @spec fib_tab(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_tab(i), do: fib_t(i, 2, %{0 => 0, 1 => 1}) @type tab() :: %{index :: integer() => value :: integer()} @spec fib_t(i :: integer(), j :: integer(), tab :: tab()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_t(i, j, tab) when i < j, do: tab[i] def fib_t(i, j, tab) do tab0 = Map.put(tab, j, tab[j-2] + tab[j-1]) fib_t(i, j+1, tab0) end end FibT.fib_tab(63) #|> IO.inspect() ``` ``` 6557470319842 ``` ```elixir defmodule FibAerl do # Tabulation (bottom-up) – dinamikus programozás # O(n) futási idő, O(n) tárhely # Erlang :array @spec fib_tab(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_tab(i), do: fib_t(i, 2, :array.set(1,1,(:array.set(0,0,:array.new())))) @type tab(integer) :: :array.arrayinteger(integer) @spec fib_t(i :: integer(), j :: integer(), tab :: tab(integer())) :: n :: integer() # n az i-edik Fibonacci-szám def fib_t(i, j, tab) when i < j, do: :array.get(i, tab) def fib_t(i, j, tab) do prev = :array.get(j-2, tab) curr = :array.get(j-1, tab) tab0 = :array.set(j, prev+curr, tab) fib_t(i, j+1, tab0) end end FibAerl.fib_tab(1023) #|> IO.inspect() ``` ``` 2785293550699592923938812412668093509353307352123703806913182668987369503203465183625616759613324452749958549669966882191117895425015208455469403731272652158240825628484818131485544230827304940519132195299466733282 ``` ```elixir defmodule FibLtab do # Tabulation (bottom-up) – dinamikus programozás # O(n) futási idő, O(n) tárhely # Elixir List @spec fib_tab(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_tab(i), do: fib_t(i, 2, [1,0]) @spec fib_t(i :: integer(), j :: integer(), tab :: [integer()]) :: n :: integer() # n az i-edik Fibonacci-szám def fib_t(i, j, tab) when i < j, do: hd(tab) def fib_t(i, j, tab) do prev = hd(tl(tab)) curr = hd(tab) tab0 = [prev+curr | tab] fib_t(i, j+1, tab0) end end FibLtab.fib_tab(63) #|> IO.inspect() ``` ``` 6557470319842 ``` ```elixir defmodule FibI do # Space optimized (bottom up) # O(n) futási idő, O(1) tárhely @spec fib_iter(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_iter(i), do: fib_i(i, 1, 0) @spec fib_i(i :: integer(), curr :: integer(), prev :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám defp fib_i(0, _curr, prev), do: prev defp fib_i(1, curr, _prev), do: curr defp fib_i(i, curr, prev), do: fib_i(i-1, prev+curr, curr) end FibI.fib_iter(2203) #|> IO.inspect() ``` ``` 11227588022178051398070623745770537746981032161033283578641889149504371902547595733548949731279174036520553510211185291521165787504965947954328861725425894532117680897067684977042503589399040135697168277407633126059586479862184815462709569351240070274187436057121550393922337505846249722123756568019538289963931388811270535294468233234206275243288823876307712381776769983580371337794399152833220102956602421639379175057893229860412359902362848104779389231572677 ``` ```elixir Benchee.run( %{ "fib tree recursive" => fn -> Fib.fib(33) end, "fib memoization" => fn -> FibM.fib_mem(33) end, "fib tabulation" => fn -> FibT.fib_tab(33) end, "fib tabula_array_erl" => fn -> FibAerl.fib_tab(33) end, "fib tabula_list" => fn -> FibLtab.fib_tab(33) end, "fib iterative" => fn -> FibI.fib_iter(33) end }, profile_after: false ) :ok ``` ``` Warning: the benchmark fib iterative is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Warning: the benchmark fib memoization is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Warning: the benchmark fib tabula_array_erl is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Warning: the benchmark fib tabula_list is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Warning: the benchmark fib tabulation is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Warning: the benchmark fib tree recursive is using an evaluated function. Evaluated functions perform slower than compiled functions. You can move the Benchee caller to a function in a module and invoke `Mod.fun()` instead. Alternatively, you can move the benchmark into a benchmark.exs file and run mix run benchmark.exs Operating System: Linux CPU Information: AMD Ryzen AI 9 HX 370 w Number of Available Cores: 24 Available memory: 93.58 GB Elixir 1.20.2 Erlang 29.0.6 JIT enabled: true Benchmark suite executing with the following configuration: warmup: 2 s time: 5 s memory time: 0 ns reduction time: 0 ns parallel: 1 inputs: none specified Estimated total run time: 42 s Excluding outliers: false Benchmarking fib iterative ... Benchmarking fib memoization ... Benchmarking fib tabula_array_erl ... Benchmarking fib tabula_list ... Benchmarking fib tabulation ... Benchmarking fib tree recursive ... Calculating statistics... Formatting results... Name ips average deviation median 99th % fib iterative 2025.02 K 0.49 μs ±1815.45% 0.47 μs 0.67 μs fib tabula_list 1793.18 K 0.56 μs ±1986.49% 0.50 μs 0.82 μs fib tabula_array_erl 507.54 K 1.97 μs ±502.71% 1.85 μs 3.01 μs fib memoization 337.87 K 2.96 μs ±255.53% 2.73 μs 5.28 μs fib tabulation 324.54 K 3.08 μs ±305.85% 2.94 μs 5.17 μs fib tree recursive 0.0504 K 19823.90 μs ±14.14% 18557.61 μs 26679.49 μs Comparison: fib iterative 2025.02 K fib tabula_list 1793.18 K - 1.13x slower +0.0638 μs fib tabula_array_erl 507.54 K - 3.99x slower +1.48 μs fib memoization 337.87 K - 5.99x slower +2.47 μs fib tabulation 324.54 K - 6.24x slower +2.59 μs fib tree recursive 0.0504 K - 40143.78x slower +19823.41 μs ``` ``` :ok ``` ```elixir # Módosított változat a memoizálási lépések követésére defmodule FibMm do @spec fib_mem(i :: integer()) :: mem :: %{integer() => integer()} def fib_mem(i), do: FibM.fib_m(i, %{0 => 0, 1 => 1}) |> elem(1) end FibMm.fib_mem(5) ``` ``` %{0 => 0, 1 => 1, 2 => 1, 3 => 2, 4 => 3, 5 => 5} ``` ```elixir # Interaktív bemenet (n slider); önálló cellába kell rakni cell = Kino.Input.number("Fibonacci index", default: 10, min: 0, max: 35) ``` ```elixir # Bemenet beolvasása index = cell |> IO.inspect(label: "Kino input cell") |> Kino.Input.read() |> IO.inspect(label: "Kino input read") ``` ``` Kino input cell: %Kino.Input{ ref: "n6mjnbwvdjjtc7mhxwkio5jbfil3ruoh", id: "130796645", destination: {Kino.SubscriptionManager, :"livebook_bm57bwqa--ad6qvet6@127.0.0.1"}, attrs: %{ default: 10, label: "Fibonacci index", max: 35, min: 0, type: :number, step: nil, debounce: :blur } } Kino input read: 10 ``` ``` 10 ``` ```elixir # Táblázat mem = FibMm.fib_mem(index) Explorer.DataFrame.new(Enum.map(mem, fn {k, v} -> %{index: k, value: inspect(%{k => v})} end)) ``` ```elixir defmodule FibTdbg do # Tabulation (bottom-up) – dinamikus programozás # O(n) futási idő, O(n) tárhely @spec fib_tab(i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_tab(i), do: fib_t(%{0 => 0, 1 => 1}, 2, i) @type fib() :: %{index :: integer() => value :: integer()} @spec fib_t(mem :: fib(), j :: integer(), i :: integer()) :: n :: integer() # n az i-edik Fibonacci-szám def fib_t(tab, j, i) when j > i, do: tab[i] def fib_t(tab, j, i) do tab |> Map.put(j, tab[j-1] + tab[j-2]) |> fib_t(j+1, i) |> dbg() end end FibTdbg.fib_tab(8) #|> IO.inspect() ``` ``` 21 ```