56 lines
1.5 KiB
Elixir
56 lines
1.5 KiB
Elixir
defmodule Bernoulli do
|
|
defmodule Rational do
|
|
import Kernel, except: [div: 2]
|
|
|
|
defstruct numerator: 0, denominator: 1
|
|
|
|
def new(numerator, denominator\\1) do
|
|
sign = if numerator * denominator < 0, do: -1, else: 1
|
|
{numerator, denominator} = {abs(numerator), abs(denominator)}
|
|
gcd = gcd(numerator, denominator)
|
|
%Rational{numerator: sign * Kernel.div(numerator, gcd),
|
|
denominator: Kernel.div(denominator, gcd)}
|
|
end
|
|
|
|
def sub(a, b) do
|
|
new(a.numerator * b.denominator - b.numerator * a.denominator,
|
|
a.denominator * b.denominator)
|
|
end
|
|
|
|
def mul(a, b) when is_integer(a) do
|
|
new(a * b.numerator, b.denominator)
|
|
end
|
|
|
|
defp gcd(a,0), do: a
|
|
defp gcd(a,b), do: gcd(b, rem(a,b))
|
|
end
|
|
|
|
def numbers(n) do
|
|
Stream.transform(0..n, {}, fn m,acc ->
|
|
acc = Tuple.append(acc, Rational.new(1,m+1))
|
|
if m>0 do
|
|
new =
|
|
Enum.reduce(m..1, acc, fn j,ar ->
|
|
put_elem(ar, j-1, Rational.mul(j, Rational.sub(elem(ar,j-1), elem(ar,j))))
|
|
end)
|
|
{[elem(new,0)], new}
|
|
else
|
|
{[elem(acc,0)], acc}
|
|
end
|
|
end) |> Enum.to_list
|
|
end
|
|
|
|
def task(n \\ 61) do
|
|
b_nums = numbers(n)
|
|
width = Enum.map(b_nums, fn b -> b.numerator |> to_string |> String.length end)
|
|
|> Enum.max
|
|
format = 'B(~2w) = ~#{width}w / ~w~n'
|
|
Enum.with_index(b_nums)
|
|
|> Enum.each(fn {b,i} ->
|
|
if b.numerator != 0, do: :io.fwrite format, [i, b.numerator, b.denominator]
|
|
end)
|
|
end
|
|
end
|
|
|
|
Bernoulli.task
|