からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

【R】sin関数の合成波の可視化

はじめに

 円関数(三角関数)の定義や性質、公式などを可視化して理解しようシリーズです。
 この記事は、「R言語 Advent Calendar 2023」の11日目の記事です。

 この記事では、sin関数(とcos関数)の合成波の定義を確認します。作図にはR言語を使います。

【前の内容】

www.anarchive-beta.com

【他の内容】

www.anarchive-beta.com

【この記事の内容】

sin関数の合成波の可視化

 円関数(circular functions)・三角関数(trigonometric functions)の1つであるsin関数(サイン関数・正弦関数・sine function)の合成波(composite wave)をグラフで確認します。
 sin関数や作図処理の詳細については「【R】sin関数の可視化 - からっぽのしょこ」、sin関数の変形については「【R】sin関数の振幅・周期・平行移動の変形の可視化 - からっぽのしょこ」を参照してください。

 利用するパッケージを読み込みます。

# 利用パッケージ
library(tidyverse)
library(gganimate)
library(patchwork)
library(magick)

 この記事では、基本的に パッケージ名::関数名() の記法を使うので、パッケージの読み込みは不要です。ただし、作図コードについてはパッケージ名を省略するので、 ggplot2 を読み込む必要があります。
 また、ネイティブパイプ演算子 |> を使います。magrittr パッケージのパイプ演算子 %>% に置き換えられますが、その場合は magrittr を読み込む必要があります。

関数(パラメータ)と曲線の形状の関係

 まずは、3つのパラメータ(振幅・周期・位相)を持つsin関数による合成波(関数の総和の曲線)への影響を確認します。

曲線の作図

 関数(パラメータ)を固定したsin関数の合成波のグラフを作成します。

・作図コード(クリックで展開)

 パラメータを指定して、関数ごとの曲線の描画用のデータフレームを作成します。

# パラメータを指定
A_vals     <- c(3, 2, 1)
a_vals     <- c(1.5, -2, 3)
alpha_vals <- c(0, 0, 0.5) * pi

# 関数の数を設定
fnc_num <- length(a_vals)

# 変数(ラジアン)の範囲を指定
theta_vec <- seq(from = -4*pi, to = 4*pi, length.out = 1000)

# 関数ごとの曲線の座標を作成
curve_each_df <- tidyr::expand_grid(
  fnc_i = 1:fnc_num, # 関数番号
  theta = theta_vec
) |> # 関数ごとにラジアンを複製
  tibble::tibble(
  A     = A_vals[fnc_i], 
  a     = a_vals[fnc_i], 
  alpha = alpha_vals[fnc_i], 
  f_t   = A * sin(a * theta + alpha)
)
curve_each_df
# A tibble: 3,000 × 6
   fnc_i theta     A     a alpha      f_t
   <int> <dbl> <dbl> <dbl> <dbl>    <dbl>
 1     1 -12.6     3   1.5     0 2.20e-15
 2     1 -12.5     3   1.5     0 1.13e- 1
 3     1 -12.5     3   1.5     0 2.26e- 1
 4     1 -12.5     3   1.5     0 3.39e- 1
 5     1 -12.5     3   1.5     0 4.51e- 1
 6     1 -12.4     3   1.5     0 5.63e- 1
 7     1 -12.4     3   1.5     0 6.73e- 1
 8     1 -12.4     3   1.5     0 7.83e- 1
 9     1 -12.4     3   1.5     0 8.92e- 1
10     1 -12.3     3   1.5     0 9.99e- 1
# ℹ 2,990 more rows

  n 個の振幅・周期・位相パラメータ  A, a, \alpha と変数(ラジアン)  \theta の範囲を指定して、 f_i(\theta) = A_i \sin(a_i \theta + \alpha_i) を計算します。
 関数の数(パラメータごとの値の数)を fnc_num としておき、関数番号とラジアンの値の組み合わせを expand_grid() で作成することで、関数ごとに曲線用のラジアンを複製して、関数の値を計算します。

 関数の総和の曲線の描画用のデータフレームを作成します。

# 関数の総和の曲線の座標を作成
curve_sum_df <- curve_each_df |> 
  dplyr::summarise(
    f_t = sum(f_t), .by = theta
  )
curve_sum_df
# A tibble: 1,000 × 2
   theta   f_t
   <dbl> <dbl>
 1 -12.6 1    
 2 -12.5 1.01 
 3 -12.5 1.01 
 4 -12.5 1.01 
 5 -12.5 1.01 
 6 -12.4 0.994
 7 -12.4 0.978
 8 -12.4 0.957
 9 -12.4 0.932
10 -12.3 0.902
# ℹ 990 more rows

  \theta の値ごとに、 n 個の関数の和  f(\theta) = \sum_{i=1}^n f_i(\theta) を計算します。

 sin関数の合成波のグラフを作成します。

# 範囲πにおける目盛数を指定
tick_num <- 1

# 目盛ラベルの範囲を設定:(π単位で切り捨て・切り上げ)
theta_lower <- floor(min(theta_vec) / pi) * pi
theta_upper <- ceiling(max(theta_vec) / pi) * pi

# ラジアン軸目盛用の値を作成
rad_break_vec <- seq(from = theta_lower, to = theta_upper, by = pi/tick_num)
rad_label_vec <- paste0(round(rad_break_vec/pi, digits = 2), " * pi")

# ラベル用の文字列を作成
param_label_vec <- paste0(
  "list(", 
  "A[", 1:fnc_num, "] == ", round(A_vals, digits = 2), ", ", 
  "a[", 1:fnc_num, "] == ", round(a_vals, digits = 2), ", ", 
  "alpha[", 1:fnc_num, "] == ", round(alpha_vals/pi, digits = 2), " * pi", 
  ")"
)

# sin関数の合成波を作図
ggplot() + 
  geom_segment(mapping = aes(x = c(-Inf, 0), y = c(0, -Inf), xend = c(Inf, 0), yend = c(0, Inf)),
               arrow = arrow(length = unit(10, units = "pt"), ends = "last")) + # x・y軸線
  geom_line(data = curve_each_df, 
            mapping = aes(x = theta, y = f_t, color = factor(fnc_i))) + # 関数ごとの曲線
  geom_line(data = curve_sum_df, 
            mapping = aes(x = theta, y = f_t), 
            linewidth = 1) + # 関数の総和の曲線
  scale_color_hue(labels = parse(text = param_label_vec), name = "parameter") + # 凡例表示用
  scale_x_continuous(breaks = rad_break_vec, 
                     labels = parse(text = rad_label_vec)) + # ラジアン軸目盛
  theme(legend.text.align = 0) + # 図の体裁
  coord_fixed(ratio = 1) + # 描画領域
  labs(title = "sine function: composite wave", 
       subtitle = expression(f(theta) == sum({}, i==1, n) * A[i]~sin(a[i]*theta + alpha[i])), 
       x = expression(theta), 
       y = expression(f(theta)))

sin関数の合成波

  n 個の関数  f_i(\theta) = A_i \sin(a_i \theta + \alpha_i) をそれぞれ色付けした実線、 n 個の曲線の合成波(関数の総和)  f(\theta) = \sum_{i=1}^n f_i(\theta) を黒色の実線で示します。 \theta はラジアン(弧度法の角度)、 \pi は円周率です。
 合成波は、変数(横軸)  \theta の値ごとに関数(縦軸)  f_i(\theta) の値を足し合わせた曲線です。

 sin関数とcos関数は  \sin(\theta + \frac{\pi}{2}) = \cos \theta の関係なので、位相パラメータを  \alpha = \frac{\pi}{2} にすると、cos関数も含めた合成波を作成できます。

アニメーションの作図

 形状が変化するsin関数の合成波のグラフを作成します。

・作図コード(クリックで展開)

 パラメータを指定して、関数ごとの曲線の描画用のデータフレームを作成します。

# フレーム数を指定
frame_num <- 101

# パラメータの範囲を指定
A_vals_mat <- cbind(
  seq(from = -1, to = 2, length.out = frame_num), 
  seq(from = 2, to = 2, length.out = frame_num), 
  seq(from = -4, to = -4, length.out = frame_num)
)
a_vals_mat <- cbind(
  seq(from = -2, to = 4, length.out = frame_num), 
  seq(from = -2, to = -2, length.out = frame_num), 
  seq(from = 2, to = 1, length.out = frame_num)
)
alpha_vals <- c(0, 0, 0.5) * pi

# 関数の数を設定
fnc_num <- length(alpha_vals)

# 変数(ラジアン)の範囲を指定
theta_vec <- seq(from = -4*pi, to = 4*pi, length.out = 1000)

# 関数ごとの曲線の座標を作成
anim_curve_each_df <- tidyr::expand_grid(
  frame_i = 1:frame_num, # フレーム番号
  fnc_i   = 1:fnc_num, # 関数番号
  theta   = theta_vec
) |> # 関数ごとにラジアンを複製
  dplyr::mutate(
    A     = as.numeric(A_vals_mat[frame_i, fnc_i]), 
    a     = as.numeric(a_vals_mat[frame_i, fnc_i]), 
    alpha = alpha_vals[fnc_i], 
    f_t   = A * sin(a * theta + alpha), 
    .by = c(frame_i, fnc_i, theta)
  )
anim_curve_each_df
# A tibble: 303,000 × 7
   frame_i fnc_i theta     A     a alpha      f_t
     <int> <int> <dbl> <dbl> <dbl> <dbl>    <dbl>
 1       1     1 -12.6    -1    -2     0 9.80e-16
 2       1     1 -12.5    -1    -2     0 5.03e- 2
 3       1     1 -12.5    -1    -2     0 1.00e- 1
 4       1     1 -12.5    -1    -2     0 1.50e- 1
 5       1     1 -12.5    -1    -2     0 2.00e- 1
 6       1     1 -12.4    -1    -2     0 2.49e- 1
 7       1     1 -12.4    -1    -2     0 2.97e- 1
 8       1     1 -12.4    -1    -2     0 3.45e- 1
 9       1     1 -12.4    -1    -2     0 3.92e- 1
10       1     1 -12.3    -1    -2     0 4.38e- 1
# ℹ 302,990 more rows

 フレーム数 frame_num を指定して、関数ごとにパラメータの値を frame_num 個または 1 個作成します。fnc_num * frame_num 個の値を指定する場合は、フレームごとの値が行で関数ごとの値が列のマトリクスとして作成しておきます。
 フレーム番号・関数番号・ラジアンの値の組み合わせを expand_grid() で作成することで、フレーム(パラメータの値)と関数の組み合わせごとに曲線用のラジアンを複製して、関数の値を計算します。

 関数の総和の曲線の描画用のデータフレームを作成します。

# 関数の総和の曲線の座標を作成
anim_curve_sum_df <- anim_curve_each_df |> 
  dplyr::summarise(
    f_t = sum(f_t), .by = c(frame_i, theta)
  )
anim_curve_sum_df
# A tibble: 101,000 × 3
   frame_i theta   f_t
     <int> <dbl> <dbl>
 1       1 -12.6 -4   
 2       1 -12.5 -4.05
 3       1 -12.5 -4.08
 4       1 -12.5 -4.10
 5       1 -12.5 -4.12
 6       1 -12.4 -4.12
 7       1 -12.4 -4.12
 8       1 -12.4 -4.10
 9       1 -12.4 -4.07
10       1 -12.3 -4.03
# ℹ 100,990 more rows

 フレームと変数の値の組み合わせごとに、関数の総和を計算します。

 sin関数の合成波のアニメーションを作成します。

# sin関数の合成波のアニメーションを作図
anim <- ggplot() + 
  geom_line(data = anim_curve_each_df, 
            mapping = aes(x = theta, y = f_t, color = factor(fnc_i))) + # 関数ごとの曲線
  geom_line(data = anim_curve_sum_df, 
            mapping = aes(x = theta, y = f_t), 
            linewidth = 1) + # 関数の総和の曲線
  gganimate::transition_manual(frames = frame_i) + # フレーム切替
  scale_color_hue(labels = 1:fnc_num, name = "function") + # 凡例表示用
  scale_x_continuous(breaks = rad_break_vec, 
                     labels = parse(text = rad_label_vec)) + # ラジアン軸目盛
  theme(legend.text.align = 0) + 
  coord_fixed(ratio = 1) + 
  labs(title = "sine function: composite wave", 
       subtitle = expression(f(theta) == sum({}, i==1, n) * A[i]~sin(a[i]*theta + alpha[i])), 
       x = expression(theta), 
       y = expression(f(theta)))

# gif画像を作成
gganimate::animate(plot = anim, nframe = frame_num, fps = 10, width = 800, height = 600)

sin関数の合成波

 各関数のパラメータ(曲線の形状)の変化に応じて、合成波の形状も変化するのを確認できます。

円周上の点の関係

 次は、「原点を中心とする円周上の点」と「関数の累積和の点を中心とする円周上の点」のグラフを作成して、「関数ごとの値」と「関数の総和の値」の関係を確認します。

円周の作図

 変数を固定して円周上の点のグラフを作成します。

・作図コード(クリックで展開)

 関数とその累積和の点に対応する値を作成します。

# パラメータを指定
A_vals     <- c(3, 2, 1)
a_vals     <- c(1.5, -2, 3)
alpha_vals <- c(0, 0, 0.5) * pi

# 関数の数を設定
fnc_num <- length(a_vals)

# 点用のラジアンを指定
theta_val <- 2/3 * pi

# 円周上の点用の値を作成
point_each_df <- tibble::tibble(
  fnc_i = 1:fnc_num, 
  theta = theta_val, 
  # パラメータ
  A     = A_vals, 
  a     = a_vals, 
  alpha = alpha_vals, 
  # 原点が中心の円周上の点
  x = A * cos(a*theta + alpha), 
  y = A * sin(a*theta + alpha)
) |> 
  dplyr::mutate(
    # 累積点が中心の円周上の点
    cumsum_x = cumsum(x), 
    cumsum_y = cumsum(y), 
    # 円周の中心(1つ前の累積点)
    O_x = dplyr::lag(cumsum_x, n = 1, default = 0), 
    O_y = dplyr::lag(cumsum_y, n = 1, default = 0)
  )
point_each_df |> 
  dplyr::select(!fnc_i) # 資料作成用に間引き
# A tibble: 3 × 10
  theta     A     a alpha         x        y cumsum_x cumsum_y   O_x      O_y
  <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>    <dbl>    <dbl> <dbl>    <dbl>
1  2.09     3   1.5  0    -3   e+ 0 3.67e-16       -3 3.67e-16     0 0       
2  2.09     2  -2    0    -1.00e+ 0 1.73e+ 0       -4 1.73e+ 0    -3 3.67e-16
3  2.09     1   3    1.57  3.06e-16 1   e+ 0       -4 2.73e+ 0    -4 1.73e+ 0

 原点を中心とする円周上の点のx座標  x_i = A_i \cos(a_i \theta + \alpha_i) とy座標  y_i = A_i \sin(a_i \theta + \alpha_i) を計算して、x, y 列とします。
 点の累積和のx座標  x_j = \sum_{i=0}^j x_i とy座標  y_j = \sum_{i=0}^j y_i を計算して、cumsum_x, cumsum_y 列とします。
 1つ前の累積点を円の中心の座標  O_j = (x_{j-1}, y_{j-1}) として、O_x, O_y 列を作成します。ただし、 1 番目の関数の円の中心は原点  O_1 = (x_0, y_0) = (0, 0) とします。

 関数とその累積和の点の描画用のデータフレームを作成します。

# 配置順を指定
facet_level_vec <- c("each", "sum")

# 関数ごとの円周上の点の座標を作成
point_df <- dplyr::bind_rows(
  # 関数ごとの点
  point_each_df |> 
    dplyr::select(fnc_i, theta, x, y, O_x, O_y) |> 
    dplyr::mutate(
      O_x = 0, 
      O_y = 0, 
      facet_level = "each"
      ), 
  # 関数の累積和ごとの点
  point_each_df |> 
    dplyr::select(fnc_i, theta, x = cumsum_x, y = cumsum_y, O_x, O_y) |> 
    dplyr::mutate(
      facet_level = "sum"
    )
) |> 
  dplyr::mutate(
    facet_level = factor(facet_level, levels = facet_level_vec) # 配置順を設定
  )
point_df
# A tibble: 6 × 7
  fnc_i theta         x        y   O_x      O_y facet_level
  <int> <dbl>     <dbl>    <dbl> <dbl>    <dbl> <fct>      
1     1  2.09 -3   e+ 0 3.67e-16     0 0        each       
2     2  2.09 -1.00e+ 0 1.73e+ 0     0 0        each       
3     3  2.09  3.06e-16 1   e+ 0     0 0        each       
4     1  2.09 -3   e+ 0 3.67e-16     0 0        sum        
5     2  2.09 -4   e+ 0 1.73e+ 0    -3 3.67e-16 sum        
6     3  2.09 -4   e+ 0 2.73e+ 0    -4 1.73e+ 0 sum       

 「原点を中心とする円周上の点(関数ごとの点)」と「1つ前の関数までの累積点を中心とする円周上の点(関数の累積和ごとの点)」の座標を格納します。グラフの配置順を調整する場合は、因子レベルを指定します。

 関数の総和の点の描画用のデータフレームを作成します。

# 関数の総和の円周上の点の座標を作成
point_sum_df <- point_each_df |> 
  dplyr::summarise(
    sum_x = sum(x), 
    sum_y = sum(y), 
    .by = theta
  ) |> 
  dplyr::mutate(
    facet_level = factor("sum", levels = facet_level_vec)
  )
point_sum_df
# A tibble: 1 × 4
  theta sum_x sum_y facet_level
  <dbl> <dbl> <dbl> <fct>      
1  2.09    -4  2.73 sum       

 関数ごとの点の座標の総和を計算して、sum_x, sum_y 列とします。

 1周期分の関数とその累積和の円周に対応する値を作成します。

# 円周用の値を作成
circle_each_df <- tibble::tibble(
  fnc_i = 1:fnc_num
) |> 
  dplyr::reframe(
    theta = seq(from = 0, to = 2*pi/abs(a_vals[fnc_i]), length.out = 361), .by = fnc_i # 1周期分のラジアン
  ) |> # 関数ごとにラジアンを作成
  dplyr::mutate(
    # パラメータ
    A     = A_vals[fnc_i], 
    a     = a_vals[fnc_i], 
    alpha = alpha_vals[fnc_i], 
    # 原点が中心の円周
    x = A * cos(a*theta + alpha), 
    y = A * sin(a*theta + alpha), 
    # 円周の中心(1つ前の累積点)
    O_x = point_each_df[["O_x"]][fnc_i], 
    O_y = point_each_df[["O_y"]][fnc_i], 
    # 累積点が中心の円周
    cumsum_x = O_x + x, 
    cumsum_y = O_y + y
  )
circle_each_df
# A tibble: 1,083 × 11
   fnc_i  theta     A     a alpha     x      y   O_x   O_y cumsum_x cumsum_y
   <int>  <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl>    <dbl>    <dbl>
 1     1 0          3   1.5     0  3    0          0     0     3      0     
 2     1 0.0116     3   1.5     0  3.00 0.0524     0     0     3.00   0.0524
 3     1 0.0233     3   1.5     0  3.00 0.105      0     0     3.00   0.105 
 4     1 0.0349     3   1.5     0  3.00 0.157      0     0     3.00   0.157 
 5     1 0.0465     3   1.5     0  2.99 0.209      0     0     2.99   0.209 
 6     1 0.0582     3   1.5     0  2.99 0.261      0     0     2.99   0.261 
 7     1 0.0698     3   1.5     0  2.98 0.314      0     0     2.98   0.314 
 8     1 0.0814     3   1.5     0  2.98 0.366      0     0     2.98   0.366 
 9     1 0.0931     3   1.5     0  2.97 0.418      0     0     2.97   0.418 
10     1 0.105      3   1.5     0  2.96 0.469      0     0     2.96   0.469 
# ℹ 1,073 more rows

 関数ごとに1周期分のラジアン  0 \leq \theta \leq \frac{2 \pi}{|a_i|} を作成して、原点を中心とする円周の座標を計算します。
 円周上の点のデータ point_each_df から中心の座標を取り出して、円周の座標に加えます。

 関数とその累積和の円周の描画用のデータフレームを作成します。

# 関数ごとの円周の座標を作成
circle_df <- dplyr::bind_rows(
  # 関数ごとの円周
  circle_each_df |> 
    dplyr::select(fnc_i, theta, x, y, O_x, O_y) |> 
    dplyr::mutate(
      O_x = 0, 
      O_y = 0, 
      facet_level = "each"
    ), 
  # 関数の累積和の円周
  circle_each_df |> 
    dplyr::select(fnc_i, theta, x = cumsum_x, y = cumsum_y, O_x, O_y) |> 
    dplyr::mutate(
      facet_level = "sum"
    )
) |> 
  dplyr::mutate(
    facet_level = factor(facet_level, levels = facet_level_vec) # 配置順を設定
  )
circle_df
# A tibble: 2,166 × 7
   fnc_i  theta     x      y   O_x   O_y facet_level
   <int>  <dbl> <dbl>  <dbl> <dbl> <dbl> <fct>      
 1     1 0       3    0          0     0 each       
 2     1 0.0116  3.00 0.0524     0     0 each       
 3     1 0.0233  3.00 0.105      0     0 each       
 4     1 0.0349  3.00 0.157      0     0 each       
 5     1 0.0465  2.99 0.209      0     0 each       
 6     1 0.0582  2.99 0.261      0     0 each       
 7     1 0.0698  2.98 0.314      0     0 each       
 8     1 0.0814  2.98 0.366      0     0 each       
 9     1 0.0931  2.97 0.418      0     0 each       
10     1 0.105   2.96 0.469      0     0 each       
# ℹ 2,156 more rows

 「原点を中心とする円周」と「1つ前の関数までの累積点を中心とする円周」の座標を格納します。

 関数とその総和の関係のグラフを作成します。

# グラフサイズを設定
axis_size <- c(
  circle_df[["x"]], circle_df[["y"]]
) |>
  abs() |>
  max() |>
  ceiling()

# ラベル用の文字列を作成
var_label <- paste0(
  "theta == ", round(theta_val/pi, digits = 2), " * pi"
)
param_label_vec <- paste0(
  "list(", 
  "A[", 1:fnc_num, "] == ", round(A_vals, digits = 2), ", ", 
  "a[", 1:fnc_num, "] == ", round(a_vals, digits = 2), ", ", 
  "alpha[", 1:fnc_num, "] == ", round(alpha_vals/pi, digits = 2), " * pi", 
  ")"
)
fnc_label_vec <- c(
  "each" = "f[i](theta) == A[i] ~ sin(a[i]*theta + alpha[i])", 
  "sum"  = "g[j](theta) == sum(f[i](theta), i==1, j)"
)

# 円周上の点の関係を作図
ggplot() + 
  geom_path(data = circle_df, 
            mapping = aes(x = x, y = y, color = factor(fnc_i))) + # 円周
  geom_point(data = point_df, 
             mapping = aes(x = x, y = y, color = factor(fnc_i)), 
             size = 2.5) + # 円周上の点
  geom_segment(data = point_df, 
               mapping = aes(x = O_x, y = O_y, xend = x, yend = y, color = factor(fnc_i)), 
               arrow = arrow(length = unit(10, units = "pt"), ends = "last")) + # ベクトル
  geom_segment(data = point_sum_df, 
               mapping = aes(x = 0, y = 0, xend = sum_x, yend = sum_y), 
               arrow = arrow(length = unit(10, units = "pt"), ends = "last")) + # ベクトルの和
  scale_color_hue(labels = parse(text = param_label_vec), name = "parameter") + # 凡例表示用
  facet_wrap(facet_level ~ ., 
             labeller = labeller(facet_level = as_labeller(fnc_label_vec, label_parsed))) + # グラフを分割
  theme(legend.text.align = 0,
        legend.position = c(0, 1),
        legend.justification = c(0, 1),
        legend.background = element_rect(fill = alpha("white", alpha = 0.8))) +
  coord_fixed(ratio = 1, 
              xlim = c(-axis_size, axis_size), 
              ylim = c(-axis_size, axis_size)) + 
  labs(title = "vector sum", 
       subtitle = parse(text = var_label), 
       x = expression(x[i] == x[i-1] + A[i] ~ cos(a[i]*theta + alpha[i])), 
       y = expression(y[i] == y[i-1] + A[i] ~ sin(a[i]*theta + alpha[i])))

円周上の点と累積和の関係

 左図は  n 個の関数  f_i(\theta) = A_i \sin(a_i \theta + \alpha_i) に対応する円周と点  (x_i, y_i) 、右図は  n 個の関数の累積和  g_j(\theta) = \sum_{i=1}^j f_i(\theta) に対応する円周と点  (\sum_{i=1}^j x_i, \sum_{i=1}^j y_i) です。
 右図の円は、1つ前の累積点が中心となるように移動したものです。ただし、 1 番目の関数の円の中心は原点  O_1 = (x_0, y_0) = (0, 0) とします。
 円の中心を始点とするベクトルでも円周上の点を表します。また、 n 個の関数の和  f(\theta) = \sum_{i=1}^n f_i(\theta) を黒色のベクトルで示します。関数(ベクトル)の総和と  n 番目の累積点は一致  f(\theta) = g_n(\theta) します。
 ベクトルの和については「【Python】1.2:ベクトルの和の可視化【『スタンフォード線形代数入門』のノート】 - からっぽのしょこ」を参照してください。

アニメーションの作図

 変数の変化に応じて移動する円周上の点のグラフを作成します。
 作図コードについては「GitHub - anemptyarchive/Mathematics」を参照してください。

変数と円周上の点と累積和の関係

 x軸・y軸方向の値をそれぞれベクトルで示します。ある  \theta のときのy軸の値が、横軸が  \theta のときの曲線の縦軸の値に対応します。
  n 個の関数の値(点・ベクトル)の変化に応じて、関数の和の値も変化するのを確認できます。

円周と曲線の関係

 最後は、円周とsin関数曲線と合成波のグラフを作成して、変数と座標・関数と形状の関係を確認します。
 作図コードについては先ほどのGitHubのリンクを参照してください。

変数と座標の関係

 変数に応じて移動する「円周上の点」と「パラメータにより変形したsin関数の曲線上の点」と「sin関数の合成波の曲線上の点」のアニメーションを作成します。

変数とsin関数曲線と合成波の関係

 円の中心(関数の累積和の点)と波の中心が対応するので、点線の水平線が破線の波線の中心になります。点線の垂直線上における破線の波線の中心からの距離の総和が合成波に対応します。

・矩形波の例

変数とsin関数曲線と合成波の関係:矩形波

 矩形波(方形波)を近似するsin関数の組み合わせです。詳しくは「フーリエ級数展開」で調べてください。

関数と曲線の関係

 関数の数が変化する「パラメータにより変形したsin関数の曲線」と「sin関数の合成波の曲線」のアニメーションを作成します。

・矩形波の例

関数の数とsin関数曲線と合成波の関係:矩形波

 組み合わせる関数の数が増えるほど矩形波に近付くのを確認できます。

 この記事では、sin関数の合成波を確認しました。

 Enjoy!

参考書籍

  • 『数学ガールの物理ノート/波の重ね合わせ』結城浩,SBクリエイティブ,2022年.

おわりに

 昨年に円関数の可視化シリーズに取り組み始めた段階で、波の重ね合わせをやってみたかったのですが、三角関数の合成と合成波と波の和は違うの?なんなの??といまいち理解できず頓挫しました。最近になって今まで手を出せてなかった数学ガールを読んでみたら、雰囲気を掴めた気がしたので確認のためにもやってみました。
 本当はフーリエ級数まで理解したかったのですが、今回はチラ見せに留めておきます。昨年書いた各円関数の定義編の加筆修正の作業で手一杯なので。この記事的にも必要な内容なのでそっちを先にアップしたかったのですが、間に合いませんでした。年内には何とかしたいですが…

 2023年12月11日は、BEYOOOOONDSのメンバーでSeasoningSのリーダーの平井美葉さんの24歳のお誕生日です。

 歌ってるときや踊ってるとき、喋ってるとき、演技してるときで別人かと思うほど雰囲気が変わるので全部観てほしいです。

【他の内容】

www.anarchive-beta.com