ロン・ルオ著
三角関数は関数電卓で計算できます 三角関数は生活や仕事のあらゆる場面で使われていますが、コンピューターではどのようにして三角関数の値を計算するのでしょうか?
三角関数は本質的に直角三角形の 3 辺の 比例関係 であり、計算はあまり直感的ではありません。では、コンピュータはどのようにして三角関数の値を計算するのでしょうか?
微積分では、 テイラーの 、テイラーの展開を使用して値の近似値を取得できることがわかっています。たとえば、次のとおりです。
罪
∠
1
8
○
=
5
−
1
4
≈
0.3090169943
\sin \angle 18^{\circ} = \frac{\sqrt {5} - 1}{4} \approx 0.3090169943
罪 ∠1 8 ○ = 4 5
− 1 ≈ 0.3090169943
テイラーの公式を使用して、前者を計算します。
4
4
4 項目:
罪
バツ
≈
バツ
−
バツ
3
3
!
+
バツ
5
5
!
−
バツ
7
7
!
\sin x \approx x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!}
罪 バツ ≈ バツ − 3 ! バツ 3 + 5 ! バツ 5 − 7 ! バツ 7
代わりに次のものを取得します。
罪
∠
1
8
○
=
罪
円周率
10
=
円周率
10
−
1
6
(
円周率
10
)
3
+
1
120
(
円周率
10
)
5
−
1
5040
(
円周率
10
)
7
≈
0.30903399538
\sin \angle 18^{\circ} = \sin \frac{\pi}{10} = \frac{\pi}{10} - \frac{1}{6} (\frac{\pi}{10})^3 + \frac{1}{120} (\frac{\pi}{10})^5 - \frac{1}{5040} (\frac{\pi}{10})^7 \approx 0.30903399538
罪 ∠1 8 ○ = 罪 10 p = 10 p − 6 1 ( 10 p ) 3 + 120 1 ( 10 p ) 5 − 5040 1 ( 10 p ) 7 ≈ 0.30903399538
服用前に見える
4
4
4 精度がすでに入っている場合
1
0
−
5
10^{-5}
1 0 − 5 未満では、精度は多くの場合に 十分に高く 。
CORDIC (座標回転デジタル コンピューター) アルゴリズム 1 アルゴリズムを知る前は、電卓はテイラー公式を使用してそれを解くものだと常に考えていましたが、CORDIC アルゴリズムがコンピューターのテイラー公式 よりも はるかに効率的であることに気づいたのはつい最近のことです。
テイラー式の欠点と、CORDIC アルゴリズムがどのように機能するかを見てみましょう。
テイラーの公式の欠点
前のセクションでは、テイラー公式を使用して三角関数の値を計算する場合、多くの乗算演算を実行する必要がありました。また、電卓での乗算演算は非常に高価であり、その欠点は次のとおり です 。
展開が小さすぎると展開精度が不足し、展開が大きすぎると計算量が大きくなりすぎるため、
べき乗演算には乗算器の使用が必要であり、多くの繰り返し計算が必要です。
中間値を保存するには多くの変数が必要です。
前回の記事では、 行列は、 乗算を減らし、加算を増やすこと で計算量を大幅に削減するものであると説明しましたが、同じ考え方を使用して演算を最適化できるでしょうか ?
もちろん、今日の主役である CORDIC アルゴリズム を紹介しましょう。
CORDIC アルゴリズムを解析する
単位円上の点が
P
P
P 、その座標は次のとおりです。
(
コス
私
、
sin
θ
)
(\cos \theta , \sin \theta )
( cos θ , sin θ ) ,如下图所示:
如果我们接收一个旋转向量
M
i
n
M_{in}
M in 逆时针旋转
θ
\theta
θ ,将点
P
(
x
i
n
,
y
i
n
)
P(x_{in} , y_{in})
P ( x in , y in ) 旋转到
P
′
(
x
R
,
y
R
)
P'(x_{R} , y_{R})
P ′ ( x R , y R ) , 如下图所示:
很容易得到如下公式:
x
R
=
x
i
n
c
o
s
(
θ
)
−
y
i
n
s
i
n
(
θ
)
x_R = x_{in} cos(\theta) - y_{in} sin(\theta)
x R = x in cos ( θ ) − y in s in ( θ )
y
R
=
x
i
n
s
i
n
(
θ
)
+
y
i
n
c
o
s
(
θ
)
y_R = x_{in} sin(\theta) + y_{in} cos(\theta)
y R = x in s in ( θ ) + y in cos ( θ )
实际上由 复数运算 ,我们知道复数乘法就是幅角相加,模长相乘 。我们可以将上式写成下列矩阵运算 形式:
[
x
R
y
R
]
=
[
cos
(
θ
)
−
sin
(
θ
)
sin
(
θ
)
cos
(
θ
)
]
[
x
i
n
y
i
n
]
\begin{aligned} \begin{bmatrix} x_{R} \\ y_{R} \end{bmatrix} = \begin{bmatrix} \cos (\theta) & -\sin (\theta) \\ \sin (\theta) & \cos (\theta ) \end{bmatrix} \begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix} \end{aligned}
[ x R y R ] = [ cos ( θ ) sin ( θ ) − sin ( θ ) cos ( θ ) ] [ x in y in ]
但上式运算时,只是对向量
v
i
n
=
[
x
i
n
y
i
n
]
v_{in} = {\begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix}}
v in = [ x in y in ] 进行了线性变换 ,乘以一个旋转向量
M
i
n
M_{in}
M in ,得到了旋转后的结果:向量
v
R
=
[
x
R
y
R
]
v_{R} = {\begin{bmatrix} x_{R} \\ y_{R} \end{bmatrix}}
v R = [ x R y R ] 。
但是上式仍然需要
4
4
4 次乘法和
2
2
2 次加减法操作, 复杂度没有任何降低,那怎么办呢?
当当...当!
通过上述分析,我们已经知道可以使用有限次旋转 操作来避免复杂的乘法操作,我们修改矩阵运算公式,提取
cos
(
θ
)
\cos (\theta )
cos ( θ ) ,则公式可以修改为:
[
x
R
y
R
]
=
c
o
s
(
θ
)
[
1
−
t
a
n
(
θ
)
t
a
n
(
θ
)
1
]
[
x
i
n
y
i
n
]
\begin{bmatrix} x_{R} \\ y_{R} \end{bmatrix} = cos(\theta) \begin{bmatrix} 1 & -tan(\theta) \\ tan(\theta) & 1 \end{bmatrix} \begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix}
[ x R y R ] = cos ( θ ) [ 1 t an ( θ ) − t an ( θ ) 1 ] [ x in y in ]
如果我们选择合适的角度值
θ
i
\theta_i
θ i ,使得
tan
(
θ
i
)
=
2
−
i
,
i
=
0
,
1
,
…
,
n
\tan (\theta_{i}) = 2^{-i} , i=0, 1,\dots , n
tan ( θ i ) = 2 − i , i = 0 , 1 , … , n
这样和
tan
(
θ
i
)
\tan (\theta_{i})
tan ( θ i ) 乘法操作就变成了移位 操作,我们知道计算机中移位 操作是非常快的,就可以大大加快计算速度了。
但这里仍然有
3
3
3 个问题需要解决:
对于任意角度 ,可以通过满足条件的角度累加来得到在数学上相同的结果吗?
每次旋转得到的结果仍然需要乘以
cos
(
θ
)
\cos(\theta )
cos ( θ ) ,这部分的计算成本如何?如何计算?
因为每次旋转角度
θ
=
arctan
(
2
−
i
)
\theta = \arctan(2^{-i})
θ = arctan ( 2 − i ) ,朝着目标角度进行旋转时,可能会出现没有超过目标角度的情况,也会存在超过目标角度的情况,这种情况如何解决呢?
对于第一个问题,答案是否定 的。可以从数学上证明只有
∠
4
5
∘
\angle 45^{\circ}
∠4 5 ∘ 的倍数角才可以得到完全一致的结果。但是在工程应用中,我们只需要满足一定精度 即可,可以增加迭代次数无限逼近原始角度,如下所示提高
n
n
n 值以无限逼近原始角度。
θ
d
=
∑
i
=
0
n
θ
i
,
∀
tan
(
θ
i
)
=
2
−
i
\theta_{d} = \sum_{i=0}^{n} \theta_{i} , \forall \tan(\theta_{i}) = 2^{-i}
θ d = i = 0 ∑ n θ i , ∀ tan ( θ i ) = 2 − i
对于第二个问题,我们先来个例子,以
57.53
5
∘
57.535^{\circ}
57.53 5 ∘ 为例来看看求解过程:
57.53
5
∘
=
4
5
∘
+
26.56
5
∘
−
14.0
3
∘
57.535^{\circ} = 45^{\circ}+26.565^{\circ}-14.03^{\circ}
57.53 5 ∘ = 4 5 ∘ + 26.56 5 ∘ − 14.0 3 ∘
那么第一次旋转:
[
x
0
y
0
]
=
c
o
s
(
4
5
∘
)
[
1
−
1
1
1
]
[
x
i
n
y
i
n
]
\begin{bmatrix} x_{0} \\ y_{0} \end{bmatrix} = cos(45^{\circ}) \begin{bmatrix} 1 & -1 \\ 1 & 1 \end{bmatrix} \begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix}
[ x 0 y 0 ] = cos ( 4 5 ∘ ) [ 1 1 − 1 1 ] [ x in y in ]
第二次旋转:
[
x
1
y
1
]
=
c
o
s
(
26.56
5
∘
)
[
1
−
2
−
1
2
−
1
1
]
[
x
0
y
0
]
\begin{bmatrix} x_{1} \\ y_{1} \end{bmatrix} = cos(26.565^{\circ}) \begin{bmatrix} 1 & -2^{-1} \\ 2^{-1} & 1 \end{bmatrix} \begin{bmatrix} x_{0} \\ y_{0} \end{bmatrix}
[ x 1 y 1 ] = cos ( 26.56 5 ∘ ) [ 1 2 − 1 − 2 − 1 1 ] [ x 0 y 0 ]
第三次旋转:
[
x
2
y
2
]
=
c
o
s
(
−
14.0
3
∘
)
[
1
2
−
2
−
2
−
2
1
]
[
x
1
y
1
]
\begin{bmatrix} x_{2} \\ y_{2} \end{bmatrix} = cos(-14.03^{\circ}) \begin{bmatrix} 1 & 2^{-2} \\ -2^{-2} & 1 \end{bmatrix} \begin{bmatrix} x_{1} \\ y_{1} \end{bmatrix}
[ x 2 y 2 ] = cos ( − 14.0 3 ∘ ) [ 1 − 2 − 2 2 − 2 1 ] [ x 1 y 1 ]
综合可得:
[
x
2
y
2
]
=
c
o
s
(
4
5
∘
)
c
o
s
(
26.56
5
∘
)
c
o
s
(
−
14.0
3
∘
)
[
1
−
1
1
1
]
[
1
−
2
−
1
2
−
1
1
]
[
1
2
−
2
−
2
−
2
1
]
[
x
i
n
y
i
n
]
\begin{bmatrix} x_{2} \\ y_{2} \end{bmatrix} = cos(45^{\circ}) cos(26.565^{\circ}) cos(-14.03^{\circ}) \begin{bmatrix} 1 & -1 \\ 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & -2^{-1} \\ 2^{-1} & 1 \end{bmatrix} \begin{bmatrix} 1 & 2^{-2} \\ -2^{-2} & 1 \end{bmatrix} \begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix}
[ x 2 y 2 ] = cos ( 4 5 ∘ ) cos ( 26.56 5 ∘ ) cos ( − 14.0 3 ∘ ) [ 1 1 − 1 1 ] [ 1 2 − 1 − 2 − 1 1 ] [ 1 − 2 − 2 2 − 2 1 ] [ x in y in ]
因为
tan
(
θ
i
)
=
2
−
i
\tan (\theta_{i}) = 2^{-i}
tan ( θ i ) = 2 − i ,由三角公式可以计算出:
cos
(
θ
i
)
=
1
1
+
tan
2
(
θ
i
)
=
1
1
+
2
−
2
i
\cos(\theta_{i}) = \frac {1}{\sqrt {1 + \tan ^{2}(\theta_{i})}} = \frac {1}{\sqrt {1 + 2^{-2i}}}
cos ( θ i ) = 1 + tan 2 ( θ i )
1 = 1 + 2 − 2 i
1
令
K
i
=
cos
(
θ
i
)
K_i = \cos(\theta_{i})
K i = cos ( θ i ) ,则当进行
n
n
n 次迭代之后:
K
(
n
)
=
∏
i
=
0
n
−
1
K
i
=
∏
i
=
0
n
−
1
1
1
+
2
−
2
i
K(n) = \prod _{i=0}^{n-1}K_{i} = \prod _{i=0}^{n-1}{\frac {1}{\sqrt {1 + 2^{-2i}}}}
K ( n ) = i = 0 ∏ n − 1 K i = i = 0 ∏ n − 1 1 + 2 − 2 i
1
当
θ
i
\theta_{i}
θ i 越来越小时,
cos
θ
\cos \theta
cos θ 也越来越逼近
1
1
1 ,当迭代次数
n
→
∞
n \to \infty
n → ∞ ,
K
(
n
)
K(n)
K ( n ) 极限存在,求解可得:
K
=
lim
n
→
∞
K
(
n
)
≈
0.6072529350088812561694
K = \lim _{n \to \infty }K(n) \approx 0.6072529350088812561694
K = n → ∞ lim K ( n ) ≈ 0.6072529350088812561694
由
K
K
K 我们实际上可以得到最终的向量
v
R
v_R
v R 的模长极限为:
A
=
1
K
=
lim
n
→
∞
∏
i
=
0
n
−
1
1
+
2
−
2
i
≈
1.64676025812107
A = {\frac {1}{K}} = \lim _{n \to \infty } \prod _{i=0}^{n - 1}{\sqrt {1 + 2^{-2i}}} \approx 1.64676025812107
A = K 1 = n → ∞ lim i = 0 ∏ n − 1 1 + 2 − 2 i
≈ 1.64676025812107
实际上当迭代次数为
6
6
6 时,可以计算出缩放比例
K
K
K ,就已经精确到
0.6072
0.6072
0.6072 了,如下所示:
K
≈
c
o
s
(
4
5
∘
)
c
o
s
(
26.56
5
∘
)
×
⋯
×
c
o
s
(
0.89
5
∘
)
=
0.6072
K \approx cos(45^{\circ}) cos(26.565^{\circ}) \times \dots \times cos(0.895^{\circ}) = 0.6072
K ≈ cos ( 4 5 ∘ ) cos ( 26.56 5 ∘ ) × ⋯ × cos ( 0.89 5 ∘ ) = 0.6072
实际上,任意角度只要迭代次数超过
6
6
6 ,我们可以直接使用
K
=
0.6072
K = 0.6072
K = 0.6072 这个值。
对于第三个问题,稍微有点复杂,我们在下一节继续讲解!
角度累加
上一节遗留的问题是迭代旋转角度时,旋转角度不一定会落在目标角度内,我们需要引入一个角度误差,用来衡量旋转角度和目标角之间距离,如下所示:
θ
e
r
r
o
r
=
θ
d
−
∑
i
=
0
n
θ
i
\theta_{error} = \theta_d - \sum_{i=0}^{n} \theta_{i}
θ error = θ d − i = 0 ∑ n θ i
当
θ
e
r
r
o
r
>
0
\theta_{error} > 0
θ error > 0 时,我们应该逆时针旋转,而
θ
e
r
r
o
r
<
0
\theta_{error} < 0
θ error < 0 ,则顺时针旋转。根据精度需要,当
∣
θ
e
r
r
o
r
∣
≤
ϵ
\left | \theta_{error} \right | \le \epsilon
∣ θ error ∣ ≤ ϵ 即可退出迭代。
同时我们修改之前的公式,引入
σ
i
∈
{
+
1
,
−
1
}
\sigma_{i} \in \left \{ +1, -1 \right \}
σ i ∈ { + 1 , − 1 } ,于是可以得到最终公式:
x
[
i
+
1
]
=
x
[
i
]
−
σ
i
2
−
i
y
[
i
]
x \left [ i+1 \right ] = x \left [ i \right ] - \sigma_{i} 2^{-i} y \left [ i \right ]
x [ i + 1 ] = x [ i ] − σ i 2 − i y [ i ]
y
[
i
+
1
]
=
y
[
i
]
+
σ
i
2
−
i
x
[
i
]
y \left [ i+1 \right ] = y \left [ i \right ] + \sigma_{i} 2^{-i} x \left [ i \right ]
y [ i + 1 ] = y [ i ] + σ i 2 − i x [ i ]
z
[
i
+
1
]
=
z
[
i
]
−
σ
i
t
a
n
−
1
(
2
−
i
)
z \left [ i+1 \right ] = z \left [ i \right ] - \sigma_{i} tan^{-1} ( 2^{-i} )
z [ i + 1 ] = z [ i ] − σ i t a n − 1 ( 2 − i )
举个例子
上面讲了这么多,来个实例吧,练习巩固下知识,看看自己是否真的懂了?
计算
sin
7
0
∘
\sin 70^{\circ}
sin 7 0 ∘ 和
cos
7
0
∘
\cos 70^{\circ}
cos 7 0 ∘ 的值。
从
x
i
n
=
1
,
y
i
n
=
0
x_{in}=1, y_{in} = 0
x in = 1 , y in = 0 开始,迭代
6
6
6 次结果如下:
第
i
i
i 次迭代
σ
i
\sigma_{i}
σ i
x
[
i
]
x \left[ i \right ]
x [ i ]
y
[
i
]
y \left[ i \right ]
y [ i ]
z
[
i
]
z \left[ i \right ]
z [ i ]
-
-
1
1
1
0
0
0
7
0
∘
70^{\circ}
7 0 ∘
0
0
0
1
1
1
1
1
1
1
1
1
2
5
∘
25^{\circ}
2 5 ∘
1
1
1
1
1
1
0.5
0.5
0.5
1.5
1.5
1.5
−
1.565
1
∘
-1.5651^{\circ}
− 1.565 1 ∘
2
2
2
−
1
-1
− 1
0.875
0.875
0.875
1.375
1.375
1.375
12.471
1
∘
12.4711^{\circ}
12.471 1 ∘
3
3
3
1
1
1
0.7031
0.7031
0.7031
1.4844
1.4844
1.4844
5.346
1
∘
5.3461^{\circ}
5.346 1 ∘
4
4
4
1
1
1
0.6103
0.6103
0.6103
1.5283
1.5283
1.5283
1.769
8
∘
1.7698^{\circ}
1.769 8 ∘
5
5
5
1
1
1
0.5625
0.5625
0.5625
1.5474
1.5474
1.5474
−
0.020
1
∘
-0.0201^{\circ}
− 0.020 1 ∘
6
6
6
−
1
-1
− 1
0.5867
0.5867
0.5867
1.5386
1.5386
1.5386
0.875
1
∘
0.8751^{\circ}
0.875 1 ∘
迭代到第
6
6
6 次时,角度误差已经小于
1
∘
1^{\circ}
1 ∘ 了, 通过表格可知:
x
R
=
0.6072
×
0.5867
=
0.3562
x_{R} = 0.6072 \times 0.5867 = 0.3562
x R = 0.6072 × 0.5867 = 0.3562
y
R
=
0.6072
×
1.5386
=
0.9342
y_{R} = 0.6072 \times 1.5386 = 0.9342
y R = 0.6072 × 1.5386 = 0.9342
通过计算器可知,
cos
(
7
0
∘
)
=
0.34202
\cos(70^{\circ}) = 0.34202
cos ( 7 0 ∘ ) = 0.34202 ,
sin
(
7
0
∘
)
=
0.93969
\sin(70^{\circ}) = 0.93969
sin ( 7 0 ∘ ) = 0.93969 ,误差已经在
1
100
\frac {1}{100}
100 1 之下了,实际应用中我们会迭代
16
16
16 次,误差会非常小 。
在线 CORDIC 算法Demo
通过上面分析,我们已经知道了 CORDIC 算法的原理,下面就开始编程吧!
JavaScript でオンライン インタラクティブ バージョンを作成しました。Portal → :
反復回数とシステム ライブラリ関数を調整できます。
Math.cos
\textit{Math.cos}
Math.cos ,
数学の罪
\textit{Math.sin}
比較のためのMath.sin :
各反復の結果を表示して、Cordic アルゴリズムの反復原理を習得できます。
CORDIC アルゴリズムの利点
他のアルゴリズムに対する CORDIC アルゴリズムの利点は、次の側面に反映されています。
簡素化された演算: CORDIC アルゴリズムは主に変位、加算、減算などの単純な演算を使用し、複雑な乗算演算を回避することで計算速度を向上させます。
並列コンピューティング: CORDIC アルゴリズムの反復演算は互いに独立しており、最新のコンピューターのマルチコアの利点を活用して並列コンピューティングを実行して、コンピューティング効率をさらに向上させることができます。
ハードウェアの最適化: CORDIC アルゴリズムはハードウェア実装に適しており、専用のハードウェア回路 (FPGA など) によって高速化できるため、計算速度が大幅に向上します。
低いストレージ要件: CORDIC アルゴリズムは、事前に計算された回転角度の少数のセットを保存するだけでよく、ストレージ スペースを節約します。
反復制御:反復回数を制御することで、計算精度と計算速度のバランスをとり、要件に応じた調整が可能です。
要約する
CORDIC アルゴリズムは、三角関数の値を計算するための効率的な方法です。 従来の Taylor 拡張と比較して、シンプルさ、高効率、ストレージ要件の低さ、反復制御という利点があります。 三角関数の値を計算する必要があるアプリケーションでは、CORDIC アルゴリズムの方が高速でリソース効率が高くなります。
元のブログ Web サイトへのリンク: www.longluo.me/blog/2023/0…
参考文献
コルディック