CORDIC アルゴリズム: 三角関数の値を計算する効率的な方法

ロン・ルオ著

三角関数は関数電卓で計算できます 三角関数は生活や仕事のあらゆる場面で使われていますが、コンピューターではどのようにして三角関数の値を計算するのでしょうか?

三角関数は本質的に直角三角形の 3 辺の比例関係であり、計算はあまり直感的ではありません。では、コンピュータはどのようにして三角関数の値を計算するのでしょうか?

微積分では、テイラーの、テイラーの展開を使用して値の近似値を取得できることがわかっています。たとえば、次のとおりです。

1 8 = 5 1 4 0.3090169943 \sin \angle 18^{\circ} = \frac{\sqrt {5} - 1}{4} \approx 0.3090169943

テイラーの公式を使用して、前者を計算します。 4 4 項目:

バツ バツ バツ 3 3 + バツ 5 5 バツ 7 7 \sin x \approx x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^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

服用前に見える 4 4 精度がすでに入っている場合 1 0 5 10^{-5} 未満では、精度は多くの場合に十分に高く

CORDIC (座標回転デジタル コンピューター) アルゴリズム1アルゴリズムを知る前は、電卓はテイラー公式を使用してそれを解くものだと常に考えていましたが、CORDIC アルゴリズムがコンピューターのテイラー公式よりもはるかに効率的であることに気づいたのはつい最近のことです。

テイラー式の欠点と、CORDIC アルゴリズムがどのように機能するかを見てみましょう。

テイラーの公式の欠点

前のセクションでは、テイラー公式を使用して三角関数の値を計算する場合、多くの乗算演算を実行する必要がありました。また、電卓での乗算演算は非常に高価であり、その欠点は次のとおりです

  1. 展開が小さすぎると展開精度が不足し、展開が大きすぎると計算量が大きくなりすぎるため、
  2. べき乗演算には乗算器の使用が必要であり、多くの繰り返し計算が必要です。
  3. 中間値を保存するには多くの変数が必要です。

前回の記事では、行列は、乗算を減らし、加算を増やすことで計算量を大幅に削減するものであると説明しましたが、同じ考え方を使用して演算を最適化できるでしょうか?

もちろん、今日の主役であるCORDIC アルゴリズムを紹介しましょう。

CORDIC アルゴリズムを解析する

単位円上の点が P P 、その座標は次のとおりです。 ( コス sin θ ) (\cos \theta , \sin \theta ) ,如下图所示:

Unit Cycle

如果我们接收一个旋转向量 M i n M_{in} 逆时针旋转 θ \theta ,将点 P ( x i n , y i n ) P(x_{in} , y_{in}) 旋转到 P ( x R , y R ) P'(x_{R} , y_{R}) , 如下图所示:

CORDIC

很容易得到如下公式:

x R = x i n c o s ( θ ) y i n s i n ( θ ) x_R = x_{in} cos(\theta) - y_{in} sin(\theta)
y R = x i n s i n ( θ ) + y i n c o s ( θ ) y_R = x_{in} sin(\theta) + y_{in} cos(\theta)

实际上由 复数运算 ,我们知道复数乘法就是幅角相加,模长相乘。我们可以将上式写成下列矩阵运算形式:

[ 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}

但上式运算时,只是对向量 v i n = [ x i n y i n ] v_{in} = {\begin{bmatrix} x_{in} \\ y_{in} \end{bmatrix}} 进行了线性变换,乘以一个旋转向量 M i n M_{in} ,得到了旋转后的结果:向量 v R = [ x R y R ] v_{R} = {\begin{bmatrix} x_{R} \\ y_{R} \end{bmatrix}}

但是上式仍然需要 4 4 次乘法和 2 2 次加减法操作, 复杂度没有任何降低,那怎么办呢?

当当...当!

通过上述分析,我们已经知道可以使用有限次旋转操作来避免复杂的乘法操作,我们修改矩阵运算公式,提取 cos ( θ ) \cos (\theta ) ,则公式可以修改为:

[ 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}

如果我们选择合适的角度值 θ i \theta_i ,使得

tan ( θ i ) = 2 i , i = 0 , 1 , , n \tan (\theta_{i}) = 2^{-i} , i=0, 1,\dots , n

这样和 tan ( θ i ) \tan (\theta_{i}) 乘法操作就变成了移位操作,我们知道计算机中移位操作是非常快的,就可以大大加快计算速度了。

但这里仍然有 3 3 个问题需要解决:

  1. 对于任意角度 ,可以通过满足条件的角度累加来得到在数学上相同的结果吗?
  2. 每次旋转得到的结果仍然需要乘以 cos ( θ ) \cos(\theta ) ,这部分的计算成本如何?如何计算?
  3. 因为每次旋转角度 θ = arctan ( 2 i ) \theta = \arctan(2^{-i}) ,朝着目标角度进行旋转时,可能会出现没有超过目标角度的情况,也会存在超过目标角度的情况,这种情况如何解决呢?

CORDIC Expand

对于第一个问题,答案是否定的。可以从数学上证明只有 4 5 \angle 45^{\circ} 的倍数角才可以得到完全一致的结果。但是在工程应用中,我们只需要满足一定精度即可,可以增加迭代次数无限逼近原始角度,如下所示提高 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}

对于第二个问题,我们先来个例子,以 57.53 5 57.535^{\circ} 为例来看看求解过程:

57.53 5 = 4 5 + 26.56 5 14.0 3 57.535^{\circ} = 45^{\circ}+26.565^{\circ}-14.03^{\circ}

那么第一次旋转:

[ 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 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 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 ] = 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}

因为 tan ( θ i ) = 2 i \tan (\theta_{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}}}

K i = cos ( θ i ) K_i = \cos(\theta_{i}) ,则当进行 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}}}}

θ i \theta_{i} 越来越小时, cos θ \cos \theta 也越来越逼近 1 1 ,当迭代次数 n n \to \infty K ( n ) K(n) 极限存在,求解可得:

K = lim n K ( n ) 0.6072529350088812561694 K = \lim _{n \to \infty }K(n) \approx 0.6072529350088812561694

K K 我们实际上可以得到最终的向量 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

实际上当迭代次数为 6 6 时,可以计算出缩放比例 K K ,就已经精确到 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

实际上,任意角度只要迭代次数超过 6 6 ,我们可以直接使用 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}

θ e r r o r > 0 \theta_{error} > 0 时,我们应该逆时针旋转,而 θ e r r o r < 0 \theta_{error} < 0 ,则顺时针旋转。根据精度需要,当 θ e r r o r ϵ \left | \theta_{error} \right | \le \epsilon 即可退出迭代。

同时我们修改之前的公式,引入 σ i { + 1 , 1 } \sigma_{i} \in \left \{ +1, -1 \right \} ,于是可以得到最终公式:

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 ]
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 ]
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} )

举个例子

上面讲了这么多,来个实例吧,练习巩固下知识,看看自己是否真的懂了?

计算 sin 7 0 \sin 70^{\circ} cos 7 0 \cos 70^{\circ} 的值。

x i n = 1 , y i n = 0 x_{in}=1, y_{in} = 0 开始,迭代 6 6 次结果如下:

i i 次迭代 σ i \sigma_{i} x [ i ] x \left[ i \right ] y [ i ] y \left[ i \right ] z [ i ] z \left[ i \right ]
- - 1 1 0 0 7 0 70^{\circ}
0 0 1 1 1 1 1 1 2 5 25^{\circ}
1 1 1 1 0.5 0.5 1.5 1.5 1.565 1 -1.5651^{\circ}
2 2 1 -1 0.875 0.875 1.375 1.375 12.471 1 12.4711^{\circ}
3 3 1 1 0.7031 0.7031 1.4844 1.4844 5.346 1 5.3461^{\circ}
4 4 1 1 0.6103 0.6103 1.5283 1.5283 1.769 8 1.7698^{\circ}
5 5 1 1 0.5625 0.5625 1.5474 1.5474 0.020 1 -0.0201^{\circ}
6 6 1 -1 0.5867 0.5867 1.5386 1.5386 0.875 1 0.8751^{\circ}

迭代到第 6 6 次时,角度误差已经小于 1 1^{\circ} 了, 通过表格可知:

x R = 0.6072 × 0.5867 = 0.3562 x_{R} = 0.6072 \times 0.5867 = 0.3562
y R = 0.6072 × 1.5386 = 0.9342 y_{R} = 0.6072 \times 1.5386 = 0.9342

通过计算器可知, cos ( 7 0 ) = 0.34202 \cos(70^{\circ}) = 0.34202 sin ( 7 0 ) = 0.93969 \sin(70^{\circ}) = 0.93969 ,误差已经在 1 100 \frac {1}{100} 之下了,实际应用中我们会迭代 16 16 次,误差会非常小

在线 CORDIC 算法Demo

通过上面分析,我们已经知道了 CORDIC 算法的原理,下面就开始编程吧!

JavaScript でオンライン インタラクティブ バージョンを作成しました。Portal :

  1. 反復回数とシステム ライブラリ関数を調整できます。 Math.cos \textit{Math.cos} 数学の罪 \textit{Math.sin} :

Cordic Results

  1. 各反復の結果を表示して、Cordic アルゴリズムの反復原理を習得できます。

Cordic Iteration Results

CORDIC アルゴリズムの利点

他のアルゴリズムに対する CORDIC アルゴリズムの利点は、次の側面に反映されています。

  1. 簡素化された演算: CORDIC アルゴリズムは主に変位、加算、減算などの単純な演算を使用し、複雑な乗算演算を回避することで計算速度を向上させます。
  2. 並列コンピューティング: CORDIC アルゴリズムの反復演算は互いに独立しており、最新のコンピューターのマルチコアの利点を活用して並列コンピューティングを実行して、コンピューティング効率をさらに向上させることができます。
  3. ハードウェアの最適化: CORDIC アルゴリズムはハードウェア実装に適しており、専用のハードウェア回路 (FPGA など) によって高速化できるため、計算速度が大幅に向上します。
  4. 低いストレージ要件: CORDIC アルゴリズムは、事前に計算された回転角度の少数のセットを保存するだけでよく、ストレージ スペースを節約します。
  5. 反復制御:反復回数を制御することで、計算精度と計算速度のバランスをとり、要件に応じた調整が可能です。

要約する

CORDIC アルゴリズムは、三角関数の値を計算するための効率的な方法です。従来の Taylor 拡張と比較して、シンプルさ、高効率、ストレージ要件の低さ、反復制御という利点があります。三角関数の値を計算する必要があるアプリケーションでは、CORDIC アルゴリズムの方が高速でリソース効率が高くなります。

元のブログ Web サイトへのリンク: www.longluo.me/blog/2023/0…

参考文献

脚注

  1. コルディック

おすすめ

転載: juejin.im/post/7246942493273767994