Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Elixirで学び直す高校数学 in Elixir

high_school_math_in_elixir.livemd

Elixirで学び直す高校数学 in Elixir

Mix.install([
  {:kino_vega_lite, "~> 0.1.10"},
  {:nx, "~> 0.7.0"}
])

Introduction

Plotting graphs

my_data = %{
  x: [1, 2, 3, 4, 5, 6, 7],
  y: [64.3, 63.8, 63.6, 64.0, 63.5, 63.2, 63.1]
}
VegaLite.new(width: 800, height: 300, title: "my_data")
|> VegaLite.data_from_values(my_data, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative, scale: %{zero: false})
x = 1..11

y =
  for i <- x do
    3 * i - 24
  end

my_data = %{x: x, y: y}
VegaLite.new(width: 800, height: 300, title: "my_data")
|> VegaLite.data_from_values(my_data, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative, scale: %{zero: false})

x-axis step and sample count

np.arange vs np.linspace

iota

  • 指定した数の 0 からの行列を作成
  • PythonのarangeはNxにはないのでNx.iotaをつかって自分で書く
arange = fn start, stop, step ->
  how_many = round((stop - start) / step)

  Nx.iota({how_many})
  |> Nx.multiply(step)
  |> Nx.add(start)
end

arange.(1, 3, 0.5)
## y = 3x

start = -1.0
stop = 1.01
step = 0.01

x = arange.(start, stop, step)
y = Nx.multiply(x, 3)

my_data = %{x: Nx.to_list(x), y: Nx.to_list(y)}
## y = 3x

import Nx.Defn

defmodule List33 do
  defn triple(x) do
    3 * x
  end
end

start = -1.0
stop = 1.01
step = 0.01

x = arange.(start, stop, step)
y = List33.triple(x)

my_data = %{x: Nx.to_list(x), y: Nx.to_list(y)}
VegaLite.new(width: 800, height: 300, title: "my_data")
|> VegaLite.data_from_values(my_data, only: ["x", "y"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)

Solving linear simultaneous equations

a + b = 1

5a + b = 3

Nx.LinAlg.solve(
  # 左辺
  Nx.tensor([
    [1, 1],
    [5, 1]
  ]),
  # 右辺
  Nx.tensor([
    1,
    3
  ])
)

Perpendicular lines

import Nx.Defn

defmodule List35 do
  defn perpendicular_lines(x) do
    %{
      y1: 1 / 2 * x + 1 / 2,
      y2: -2 * x + 7
    }
  end
end

start = -1
stop = 6
step = 1

x = arange.(start, stop, step)
%{y1: y1, y2: y2} = List35.perpendicular_lines(x)

my_data1 = %{x: Nx.to_list(x), y: Nx.to_list(y1)}
my_data2 = %{x: Nx.to_list(x), y: Nx.to_list(y2)}
VegaLite.new(width: 250, height: 500)
|> VegaLite.layers([
  VegaLite.new()
  |> VegaLite.data_from_values(my_data1, only: ["x", "y"])
  |> VegaLite.mark(:line)
  |> VegaLite.encode_field(:x, "x", type: :quantitative)
  |> VegaLite.encode_field(:y, "y", type: :quantitative),
  VegaLite.new()
  |> VegaLite.data_from_values(my_data2, only: ["x", "y"])
  |> VegaLite.mark(:line)
  |> VegaLite.encode_field(:x, "x", type: :quantitative)
  |> VegaLite.encode_field(:y, "y", type: :quantitative)
])

Intersection of lines

(-3/2 * x) + (-1 * y) = -6
(1/2 * x) + (-1 * y) = -2
Nx.LinAlg.solve(
  # 左辺
  Nx.tensor([
    [-3 / 2, -1],
    [1 / 2, -1]
  ]),
  # 右辺
  Nx.tensor([
    -6,
    -2
  ])
)

Perpendicular bisector

import Nx.Defn

defmodule List42 do
  defn f(x) do
    # 基となる線分の傾きと切片
    a1 = (5 - 1) / (6 - 0)
    b1 = 1

    # 線分の中点
    cx = (0 + 6) / 2
    cy = (1 + 5) / 2

    # 線分に直交する直線の傾き
    a2 = -1 / a1

    # 線分に直交する直線の切片
    b2 = cy - a2 * cx

    # 直線の式
    %{
      y1: a1 * x + b1,
      y2: a2 * x + b2
    }
  end
end

x = arange.(0, 8, 1)
y = List42.f(x)

my_data1 = %{x: Nx.to_list(x), y: Nx.to_list(y.y1)}
my_data2 = %{x: Nx.to_list(x), y: Nx.to_list(y.y2)}
VegaLite.new(width: 300, height: 400)
|> VegaLite.layers([
  VegaLite.new()
  |> VegaLite.data_from_values(my_data1, only: ["x", "y"])
  |> VegaLite.mark(:point)
  |> VegaLite.encode_field(:x, "x", type: :quantitative)
  |> VegaLite.encode_field(:y, "y", type: :quantitative),
  VegaLite.new()
  |> VegaLite.data_from_values(my_data2, only: ["x", "y"])
  |> VegaLite.mark(:line)
  |> VegaLite.encode_field(:x, "x", type: :quantitative)
  |> VegaLite.encode_field(:y, "y", type: :quantitative)
])

Trigonometric ratios and circles

  • Nx.sin や Nx.cos に渡すラジアンを度数から計算する np.radians がNxに無いので、度数法からラジアンに変換する無名関数を作って計算

radians = (degrees / 180) * pi

# 度数をラジアンに変換する関数
radians = fn degrees ->
  degrees
  |> Nx.divide(180)
  |> Nx.multiply(Nx.Constants.pi())
end

# 角度
th = Nx.linspace(0, 360, n: 360)

# 円周上の点Pの座標
x = Nx.sin(radians.(th))
y = Nx.cos(radians.(th))

my_data = %{x: Nx.to_list(x), y: Nx.to_list(y)}
VegaLite.new(width: 500, height: 500)
|> VegaLite.data_from_values(my_data, only: ["x", "y"])
|> VegaLite.mark(:point)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)