是否有一种更有效的算法(或最有效的已知算法)来选择给定为$ I + jQ $的两个复数中的较大者,而不必计算平方幅度为

$$ I ^ 2 + Q ^ 2 $$

特别是不需要乘法器的二进制算术解决方案?这适用于使用AND,NAND,OR,NOR,XOR,XNOR,INV以及移位和加法的二进制算术解决方案,而无需简单地用其移位和加法等效项(或处理步骤中的等效项)替换所需的乘法步骤。还要假设数字是用矩形固定点(有界整数)坐标(I,Q)表示的。

我知道复数的幅度估计器,例如$ max(I,Q)+ min(I,Q)/ 2 $,以及更精确的变体,但要乘以系数,但是它们全都误差有限。

我考虑过使用CORDIC旋转器将每个旋转到实轴,然后能够比较实数。此解决方案也具有有限的误差,但是我可以选择CORDIC中的迭代次数,以使对于我在可用数值精度范围内选择的任何$ e $而言,误差小于$ e $。因此,该解决方案是可以接受的。

是否有其他解决方案比CORDIC效率更高(需要通过迭代才能获得准确性的时间)?


确定最佳答案

参与者做了很多不可思议的工作(如果有人有其他想法,我们仍然欢迎竞争性选择)。我发布了我对算法进行排名的建议方法,并在深入研究之前欢迎有关排名方法的辩论:

对复杂幅值比较问题进行排名的最佳方法

评论

我确定您知道,但是平方根当然是非负实数上的严格单调函数,因此可以将其省略以进行比较。

是更有效的方法来计算$ I ^ 2 + Q ^ 2 $ OK吗?

我不确定所需的精度和乘法运算的成本,但是我想说对于大多数实际应用而言,alpha_max_plus_beta_min算法是一个不错的选择。

优秀的名字参考马特!我一直在寻找它,因为我在旧的“工程师公式”书中看到了这个0.96a + 0.4b $的近似值

@MattL。这是我在我的评论“我知道幅度估计器”中的引用-我的那些问题(据我了解)是,我不能仅凭2个因素就将给定e的误差减小到小于e我正在寻找一种可以用于任何给定精度的解决方案,但要付出更多的努力,就需要更高的精度。我对alpha max beta min的限制是否正确? (感谢命名)

#1 楼

您在评论中提到目标平台是定制IC。这使得优化与尝试针对已经存在的CPU进行优化有很大不同。在定制IC(在较小程度上,在FPGA上)上,我们可以充分利用按位操作的简单性。此外,要减小面积,不仅要减少我们执行的操作,而且要使用相同的逻辑来执行尽可能多的操作,不仅很重要。

诸如VHDL之类的语言中的逻辑描述可以通过合成器工具转换为逻辑门网表,该工具可以为我们完成大多数底层优化。我们需要做的是选择一种可以实现我们要优化的目标的体系结构。我将在下面显示几种替代方法。

单周期计算:最低延迟

如果要在单个周期中获得结果,则基本上都可以归结为大型组合逻辑功能。这正是综合工具最擅长的解决方案,因此您可以尝试将基本方程式放在合成器上:



看看它能给什么。许多合成器都具有一些属性,您可以使用这些属性来强制它们执行逻辑门级优化,而不是使用乘法器和加法器宏。区域单循环解决方案。合成器可以进行大量优化,例如利用$ x \ cdot x $的对称性,而不是通用$ x \ cdot y $乘数。

流水线式计算:最大吞吐量

处理单周期计算将提高最大时钟速度,从而提高吞吐量,但不会减少所需的面积。对于基本流水线,您可以从最低有效位开始计算每个流水线级别上每个量级的N位。在最简单的情况下,您可以将其分为两半:

if I1*I1 + Q1*Q1 > I2*I2 + Q2*Q2 then ...


为什么从最低有效位开始?由于它们的逻辑方程最短,因此计算速度更快。最高有效位的结果仅在第二个时钟周期被馈入比较器,从而有更多时间进行组合逻辑。

对于超过2级的流水线,进位将分别在各个阶段之间传递。这将消除通常会限制乘法时钟速率的长纹波进位链。

以最高有效位开始计算可以允许尽早终止,但在流水线配置中难以利用

序列化计算,LSB在先:小面积

序列化计算会减少所需的面积,但每个值都需要多个周期在开始下一次计算之前进行处理。

最小的区域选项是在每个时钟周期计算2个最低有效位。在下一个周期,I和Q值将右移1位,从而使平方的值移位2位。这样,仅需要2x2位乘法器,占用的芯片面积就很小。

为了避免存储完整的幅度值,还可以将比较序列化,记住LSB结果,如果MSB位不同则用MSB结果覆盖它。

-- Second pipeline stage
if m1(N downto N/2) > m2(N downto N/2) then ...

-- First pipeline stage
m1 := I1*I1 + Q1*Q1;
m2 := I2*I2 + Q2*Q2;
if m1(N/2-1 downto 0) > m2(N/2-1 downto 0) then ...

串行化计算,MSB优先:小面积,提前终止

如果我们修改串行化计算以处理从最高有效位开始的输入数据,则可以终止为一旦发现差异就可以了。

这确实会导致进位逻辑的复杂化:最高位可以通过低位的进位来改变。但是,可以预测其对比较结果的影响。如果每个幅度的高位相等,我们就只能得到低位。然后,如果一个量级具有进位,而另一个量级没有进位,则该值必然更大。如果两个幅度的进位相等,则高位也将保持相等。

m1 := I1(1 downto 0) * I1(1 downto 0) + Q1(1 downto 0) * Q1(1 downto 0) + m1(3 downto 2);
m2 := I2(1 downto 0) * I2(1 downto 0) + Q2(1 downto 0) * Q2(1 downto 0) + m2(3 downto 2);
I1 := shift_right(I1, 1); Q1 := shift_right(Q1, 1);
I2 := shift_right(I2, 1); Q2 := shift_right(Q2, 1);
if m1 > m2 then
    result := 1;
elif m1 < m2 then
    result := 0;
else
    -- keep result from LSBs
end if;


MSB优先和LSB优先的串行化解决方案可能具有近似相等的值


在每个代码示例中,我都依靠合成器将逻辑门级的乘法优化为二进制操作。应该选择操作数的宽度,以使计算不会溢出。

编辑:经过一整夜的思考,我不再确保对数字进行平方可以完全序列化或只完成一次2位。因此,毕竟串行化的实现可能必须诉诸于N位累加器。

#2 楼

序言

我对这个问题的回答分为两部分,因为它是如此之长,而且自然断裂。这个答案可以看作是主体,其他的答案可以看作是附录。考虑一下这是将来博客文章的粗略草稿。

Answer 1
  * Prologue (you are here)
  * Latest Results
  * Source code listing
  * Mathematical justification for preliminary checks

Answer 2
  * Primary determination probability analysis
  * Explanation of the lossless adaptive CORDIC algorithm
  * Small angle solution  


比起最初出现的结果,这是一种更深入和有趣的问题。这里给出的答案是新颖新颖的。我也非常感兴趣它是否存在于任何教规中。

过程可以总结如下:


初步确定。这样可以非常有效地捕获大约80%的情况。
将点移动到差/和空间,并测试了一系列条件。除了约1%的病例外,其余所有病例均如此。仍然相当有效。
将结果差/和对移回IQ空间,并尝试使用自定义的CORDIC方法。

到目前为止,结果是100%准确的。有一种可能是弱点的情况,我现在正在测试候选人是否要变强。准备就绪后,它将被合并到此答案的代码中,并在另一个答案中添加说明。


代码已更新。现在,它报告出口位置计数。在功能定义中注释位置点。最新结果:
   Count: 1048576

    Sure: 100.0
 Correct: 100.0

Presumed: 0.0
Actually: -1

  Faulty: 0.0

    High: 1.0
     Low: 1.0

1   904736   86.28
2     1192   86.40
3     7236   87.09
4    13032   88.33
5   108024   98.63
6     8460   99.44


如果我的算法不允许进入CORDIC例程,但假定答案为零(正确假设为8.4%),则为以下结果。总体正确率为99.49%(100-0.51错误)。


   Count: 1048576

    Sure: 99.437713623
 Correct: 100.0

Presumed: 0.562286376953
Actually: 8.41248303935

  Faulty: 0.514984130859

    High: 1.05125
     Low: 0.951248513674

1   904736   86.28
2     1192   86.40
3     7236   87.09
4    13032   88.33
5   108024   98.63
6     8460   99.44




好吧,我添加了Olli算法的整数解释。我真的很感谢有人仔细检查我对Python的翻译。它位于源代码的末尾。

结果如下:

   Count: 1048576

 Correct: 94.8579788208

1   841216   80.22  (Partial) Primary Determination
2    78184   87.68  1st CORDIC
3   105432   97.74  2nd 
4    10812   98.77  3rd
5        0   98.77  4th
6    12932  100.00  Terminating Guess


接下来,我在主要斜率线比较中添加了“ =”。这是我留在代码中的版本。

结果得到了改善。要自己进行测试,只需更改main()例程中正在调用的函数即可。您可以随心所欲地玩转它。如果有人发现任何错误,请通知我。

 Correct: 95.8566665649

1   861056   82.12
2   103920   92.03
3    83600  100.00



要避免乘法。

出于比较的目的,不仅不必取平方根,还可以使用绝对值。



$$
\ begin {aligned}
a_1&= | I_1 | \\
b_1&= | Q_1 | \\
a_2&= | I_2 | \\
b_2&= | Q_2 | \\
\ end {aligned}

请注意,对于$ a,b \ ge 0 $:

$$(a + b)^ 2 \ ge a ^ 2 + b ^ 2 $$

因此
$$ a_1> a_2 + b_2 $$
表示

$$ a_1 ^ 2 + b_1 ^ 2 \ ge a_1 ^ 2>(a_2 + b_2)^ 2 \ ge a_2 ^ 2 + b_2 ^ 2 $$

$$ a_1 ^ 2 + b_1 ^ 2> a_2 ^ 2 + b_2 ^ 2 $$

$ b_1 $也是如此。同样在另一个方向上,导致了这种逻辑:

(以前的伪代码在功能上已由下面的Python列表代替。)值,这样可以节省很多。但是,如果期望所有值都接近,那么最好屈服并从一开始就评估Else子句。您可以通过不计算s1来进行稍微优化,除非需要它。

这已经超出了我的头脑,所以我不能告诉您它是否是最好的。根据值的范围,查找表也可能会起作用,但是内存获取可能比计算昂贵。


这应该更有效地运行:

(以前的伪代码在功能上已由下面的Python列表代替。)

更多逻辑:

$$
\ begin {aligned}
(a_1 ^ 2 + b_1 ^ 2)-(a_2 ^ 2 + b_2 ^ 2)&=(a_1 ^ 2-a_2 ^ 2)+(b_1 ^ 2-b_2 ^ 2)\ \
&=(a_1-a_2)(a_1 + a_2)+(b_1-b_2)(b_1 + b_2)\\
\ end {aligned}
$$

当$ a_1> a_2 $(和$ a_1 \ ge b_1 $和$ a_2 \ ge b_2 $如代码中一样)时:

$$(a_1-a_2)(a_1 + a_2) +(b_1-b_2)(b_1 + b_2)> =(a1-a2)(b1 + b2)+(b1-b2)(b1 + b2)= [(a1 + b1)-(a2 + b2)](b1 + b2)$$

因此,如果$ a_1 + b_1> a_2 + b_2 $,则

$$(a_1 ^ 2 + b_1 ^ 2)-(a_2 ^ 2 + b_2 ^ 2)> 0 $$

含义1更大。

对于$ a_1
反之亦然被修改。这使得需求确定案例的规模很小。还在修补。...

今晚放弃。请注意,$ a $值比较之后的$ b $值比较实际上并入随后的总和比较中。我将它们保留在代码中,因为它们节省了两个和。因此,您正在赌博一个if,以节省一个if和两个和。汇编语言思维。

如果没有“乘法”,我看不到该怎么做。我将其用引号引起来,因为我现在试图提出某种局部乘法方案,该方案只需走得足够远即可确定。肯定会迭代的。也许等效于CORDIC。


好吧,我想我大部分都明白了。

我将展示$ a_1> a_2 $的情况。小于情况的工作原理相同,只是您的结论相反。



$$
d_a&= a_1 -a_2 \\
s_b&= b_2 + b_1 \\
\ end {aligned}
$$

在“需要确定”的情况下,所有这些值都将大于零。

观察:

$$
\ begin {aligned}
D&=(a_1 ^ 2 + b_1 ^ 2)-(a_2 ^ 2 + b_2 ^ 2)\\
&=(a_1 ^ 2-a_2 ^ 2 )+(b_1 ^ 2-b_2 ^ 2)\\
&=(a_1-a_2)(a_1 + a_2)+(b_1-b_2)(b_1 + b_2)\\
&=(a_1-a_2)(a_1 + a_2)-(b_2-b_1)(b_1 + b_2)\\
&= d_a s_a-d_b s_b
\ end {aligned}
$ $

现在,如果$ D = 0 $,则1和2相等。如果$ D> 0 $,则1更大。否则,2会更大。

这是技巧的“ CORDIC”部分:

import array as arr

#================================================
def Main():

#---- Initialize the Counters

        theCount      = 0
        theWrongCount = 0

        thePresumedZeroCount    = 0
        theCloseButNotZeroCount = 0

        theTestExits = arr.array( "i", [ 0, 0, 0, 0, 0, 0, 0 ] )

#---- Test on a Swept Area

        theLimit = 32
        theLimitSquared = theLimit * theLimit

        theWorstHigh = 1.0
        theWorstLow  = 1.0

        for i1 in range( theLimit ):
          ii1 = i1 * i1
          for q1 in range( theLimit ):
            m1 = ii1 + q1 * q1
            for i2 in range( theLimit ):
              ii2 = i2 * i2
              for q2 in range( theLimit ):
                m2 = ii2 + q2 * q2
                D  = m1 - m2

                theCount += 1

                c, t = CompareMags( i1, q1, i2, q2 )

                if t <= 6:
                   theTestExits[t] += 1

                if c == 2:

                   thePresumedZeroCount += 1
                   if D != 0:
                      theCloseButNotZeroCount += 1

                      Q = float( m1 ) / float( m2 )
                      if Q > 1.0:
                         if theWorstHigh < Q:
                            theWorstHigh = Q
                      else:
                         if theWorstLow > Q:
                            theWorstLow = Q

                      print "%2d  %2d   %2d  %2d   %10.6f" % ( i1, q1, i2, q2, Q )

                elif c == 1:
                   if D <= 0:
                      theWrongCount += 1
                      print "Wrong Less ", i1, q1, i2, q2, D, c
                elif c == 0:
                   if D != 0:
                      theWrongCount += 1
                      print "Wrong Equal", i1, q1, i2, q2, D, c
                elif c == -1:
                   if D >= 0:
                      theWrongCount += 1
                      print "Wrong Great", i1, q1, i2, q2, D, c
                else:
                   theWrongCount += 1
                   print "Invalid c value:", i1, q1, i2, q2, D, c


#---- Calculate the Results

        theSureCount   = ( theCount - thePresumedZeroCount )                    
        theSurePercent = 100.0 * theSureCount / theCount

        theCorrectPercent = 100.0 * ( theSureCount - theWrongCount ) \
                          / theSureCount

        if thePresumedZeroCount > 0:
           theCorrectPresumptionPercent = 100.0 * ( thePresumedZeroCount - theCloseButNotZeroCount ) \
                                        / thePresumedZeroCount
        else:
           theCorrectPresumptionPercent = -1

        theFaultyPercent = 100.0 * theCloseButNotZeroCount / theCount

#---- Report the Results

        print
        print "   Count:", theCount
        print
        print "    Sure:", theSurePercent
        print " Correct:", theCorrectPercent
        print
        print "Presumed:", 100 - theSurePercent
        print "Actually:", theCorrectPresumptionPercent
        print
        print "  Faulty:", theFaultyPercent
        print
        print "    High:", theWorstHigh
        print "     Low:", theWorstLow
        print

#---- Report The Cutoff Values

        pct = 0.0
        f   = 100.0 / theCount

        for t in range( 1, 7 ):
          pct += f * theTestExits[t]
          print "%d %8d  %6.2f" % ( t, theTestExits[t], pct )

        print

#================================================
def CompareMags( I1, Q1, I2, Q2 ):

# This function compares the magnitudes of two 
# integer points and returns a comparison result value
#
# Returns  ( c, t )
#
#    c   Comparison
#
#   -1     | (I1,Q1) |  <  | (I2,Q2) |
#    0     | (I1,Q1) |  =  | (I2,Q2) |
#    1     | (I1,Q1) |  >  | (I2,Q2) |
#    2     | (I1,Q1) | ~=~ | (I2,Q2) |
#
#    t   Exit Test
#
#    1     Primary Determination
#    2     D/S Centers are aligned
#    3     Obvious Answers
#    4     Trivial Matching Gaps
#    5     Opposite Gap Sign Cases
#    6     Same Gap Sign Cases
#   10     Small Angle + Count
#   20     CORDIC + Count
#
# It does not matter if the arguments represent vectors 
# or complex numbers.  Nor does it matter if the calling
# routine considers the integers as fixed point values.


#---- Ensure the Points are in the First Quadrant WLOG

        a1 = abs( I1 )
        b1 = abs( Q1 )

        a2 = abs( I2 )
        b2 = abs( Q2 )

#---- Ensure they are in the Lower Half (First Octant) WLOG

        if b1 > a1:
           a1, b1 = b1, a1

        if b2 > a2:
           a2, b2 = b2, a2

#---- Primary Determination

        if a1 > a2:
           if a1 + b1 >= a2 + b2:
              return 1, 1
           else:
              thePresumedResult = 1
              da = a1 - a2
              sa = a1 + a2
              db = b2 - b1
              sb = b2 + b1
        elif a1 < a2:
           if a1 + b1 <= a2 + b2:
              return -1, 1
           else:
              thePresumedResult = -1
              da = a2 - a1
              sa = a2 + a1
              db = b1 - b2
              sb = b1 + b2
        else:
           if b1 > b2:
              return 1, 1
           elif b1 < b2:
              return -1, 1
           else:
              return 0, 1

#---- Bring Factors into 1/2 to 1 Ratio Range

        db, sb = sb, db

        while da < sa:
            da += da
            sb += sb
            if sb > db:
               db, sb = sb, db

#---- Ensure the [b] Factors are Both Even or Odd

        if ( ( sb + db ) & 1 ) > 0:
           da += da
           sa += sa
           db += db
           sb += sb

#---- Calculate Arithmetic Mean and Radius of [b] Factors

        p = ( db + sb ) >> 1
        r = ( db - sb ) >> 1

#---- Calculate the Gaps from the [b] mean and [a] values

        g = da - p
        h = p - sa

#---- If the mean of [b] is centered in (the mean of) [a]

        if g == h:
           if g == r:
              return 0, 2;
           elif g > r:
              return -thePresumedResult, 2
           else:
              return thePresumedResult, 2

#---- Weed Out the Obvious Answers

        if g > h:
           if r > g and r > h:
              return thePresumedResult, 3
        else:             
           if r < g and r < h:
              return -thePresumedResult, 3

#---- Calculate Relative Gaps

        vg = g - r
        vh = h - r

#---- Handle the Trivial Matching Gaps

        if vg == 0:
           if vh > 0:
              return -thePresumedResult, 4
           else:
              return thePresumedResult, 4

        if vh == 0:
           if vg > 0:
              return thePresumedResult, 4
           else:
              return -thePresumedResult, 4


#---- Handle the Gaps with Opposite Sign Cases

        if vg < 0:
           if vh > 0:
              return -thePresumedResult, 5
        else:
           if vh < 0:
              return thePresumedResult, 5

#---- Handle the Gaps with the Same Sign (using numerators)

        theSum = da + sa

        if g < h:
           theBound = ( p << 4 ) - p  
           theMid   = theSum << 3

           if theBound > theMid:
              return -thePresumedResult, 6
        else:
           theBound = ( theSum << 4 ) - theSum
           theMid   = p << 5

           if theBound > theMid:
              return thePresumedResult, 6

#---- Return to IQ Space under XY Names

        x1 = theSum
        x2 = da - sa

        y2 = db + sb
        y1 = db - sb

#---- Ensure Points are in Lower First Quadrant (First Octant)

        if x1 < y1:
           x1, y1 = y1, x1

        if x2 < y2:
           x2, y2 = y2, x2

#---- Variation of Olli's CORDIC to Finish

        for theTryLimit in range( 10 ):
            c, x1, y1, x2, y2 = Iteration( x1, y1, x2, y2, thePresumedResult )
            if c != 2:
               break

            if theTryLimit > 3:   
               print "Many tries needed!", theTryLimit, x1, y1, x2, y2

        return c, 20

#================================================
def Iteration( x1, y1, x2, y2, argPresumedResult ):

#---- Try to reduce the Magnitudes

        while ( x1 & 1 ) == 0 and \
              ( y1 & 1 ) == 0 and \
              ( x2 & 1 ) == 0 and \
              ( y2 & 1 ) == 0:
            x1 >>= 1
            y1 >>= 1
            x2 >>= 1
            y2 >>= 1

#---- Set the Perpendicular Values (clockwise to downward)

        dx1 =  y1
        dy1 = -x1

        dx2 =  y2
        dy2 = -x2

        sdy = dy1 + dy2

#---- Allocate the Arrays for Length Storage

        wx1 = arr.array( "i" )
        wy1 = arr.array( "i" )
        wx2 = arr.array( "i" )
        wy2 = arr.array( "i" )

#---- Locate the Search Range

        thePreviousValue = x1 + x2  # Guaranteed Big Enough

        for theTries in range( 10 ):
            wx1.append( x1 )
            wy1.append( y1 )
            wx2.append( x2 )
            wy2.append( y2 )

            if x1 > 0x10000000 or x2 > 0x10000000:
               print "Danger, Will Robinson!"
               break

            theValue = abs( y1 + y2 + sdy )

            if theValue > thePreviousValue:
               break

            thePreviousValue = theValue

            x1 += x1
            y1 += y1
            x2 += x2
            y2 += y2

#---- Prepare for the Search

        theTop = len( wx1 ) - 1

        thePivot = theTop - 1

        x1 = wx1[thePivot]
        y1 = wy1[thePivot]
        x2 = wx2[thePivot]
        y2 = wy2[thePivot]

        theValue = abs( y1 + y2 + sdy )

#---- Binary Search

        while thePivot > 0:
            thePivot -= 1

            uy1 = y1 + wy1[thePivot]
            uy2 = y2 + wy2[thePivot]

            theUpperValue = abs( uy1 + uy2 + sdy )

            ly1 = y1 - wy1[thePivot]
            ly2 = y2 - wy2[thePivot]

            theLowerValue = abs( ly1 + ly2 + sdy )

            if theUpperValue < theLowerValue:
               if theUpperValue < theValue:
                  x1 += wx1[thePivot]
                  x2 += wx2[thePivot]
                  y1  = uy1
                  y2  = uy2

                  theValue = theUpperValue

            else:
               if theLowerValue < theValue:
                  x1 -= wx1[thePivot]
                  x2 -= wx2[thePivot]
                  y1  = ly1
                  y2  = ly2

                  theValue = theLowerValue

#---- Apply the Rotation

        x1 += dx1
        y1 += dy1

        x2 += dx2
        y2 += dy2

#---- Bounce Points Below the Axis to Above

        if y1 < 0:
           y1 = -y1

        if y2 < 0:
           y2 = -y2

#---- Comparison Determination

        c = 2

        if x1 > x2:
           if x1 + y1 >= x2 + y2:
              c = argPresumedResult
        elif x1 < x2:
           if x1 + y1 <= x2 + y2:
              c = -argPresumedResult
        else:
           if y1 > y2:
              c = argPresumedResult
           elif y1 < y2:
              c = -argPresumedResult
           else:
              c =  0

#---- Exit

        return c, x1, y1, x2, y2

#================================================
def MyVersionOfOllis( I1, Q1, I2, Q2 ):

# Returns  ( c, t )
#
#    c   Comparison
#
#   -1     | (I1,Q1) |  <  | (I2,Q2) |
#    1     | (I1,Q1) |  >  | (I2,Q2) |
#
#    t   Exit Test
#
#    1     (Partial) Primary Determination
#    2     CORDIC Loop + 1
#    6     Terminating Guess

#---- Set Extent Parameter

        maxIterations = 4

#---- Ensure the Points are in the First Quadrant WLOG

        I1 = abs( I1 )
        Q1 = abs( Q1 )

        I2 = abs( I2 )
        Q2 = abs( Q2 )

#---- Ensure they are in the Lower Half (First Octant) WLOG

        if Q1 > I1:
           I1, Q1 = Q1, I1

        if Q2 > I2:
           I2, Q2 = Q2, I2

#---- (Partial) Primary Determination

        if I1 < I2 and  I1 + Q1 <= I2 + Q2:
           return -1, 1

        if I1 > I2 and  I1 + Q1 >= I2 + Q2:
           return 1, 1

#---- CORDIC Loop

        Q1pow1 = Q1 >> 1
        I1pow1 = I1 >> 1
        Q2pow1 = Q2 >> 1
        I2pow1 = I2 >> 1

        Q1pow2 = Q1 >> 3
        I1pow2 = I1 >> 3
        Q2pow2 = Q2 >> 3
        I2pow2 = I2 >> 3

        for n in range ( 1, maxIterations+1 ):
            newI1 = I1 + Q1pow1
            newQ1 = Q1 - I1pow1
            newI2 = I2 + Q2pow1
            newQ2 = Q2 - I2pow1

            I1 = newI1
            Q1 = abs( newQ1 )
            I2 = newI2
            Q2 = abs( newQ2 )

            if I1 <= I2 - I2pow2:
               return -1, 1 + n

            if I2 <= I1 - I1pow2:
               return 1, 1 + n

            Q1pow1 >>= 1
            I1pow1 >>= 1
            Q2pow1 >>= 1
            I2pow1 >>= 1

            Q1pow2 >>= 2
            I1pow2 >>= 2
            Q2pow2 >>= 2
            I2pow2 >>= 2

#---- Terminating Guess

        Q1pow1 <<= 1
        Q2pow1 <<= 1

        if I1 + Q1pow1 < I2 + Q2pow1:
           return -1, 6
        else:   
           return 1, 6

#================================================
Main()


此循环完成时,以下内容has是真实的:

$ D $已乘以2的幂,保留了符号指示。

$$ 2 2 $$

$$ 2 s_b> d_b \ ge s_b> d_b / 2 $$

换句话说,$ d $将大于$ s $,并且它们将在彼此的两倍之内。

由于我们正在使用整数,因此下一步要求$ d_b $和$ s_b $均为偶数或奇数。
Swap db, sb   # d is now the larger quantity

While da < sa
  da =<< 1
  sb =<< 1
  If sb > db Then Swap db, sb # s is the smaller quantity
EndWhile


这会将$ D $值乘以4,但再次保留符号指示。


$$
\开始{aligned}
p&=(d_b + s_b)>> 1 \\
r&=(d_b-s_b)>> 1 \\
\ end {aligned}
$$

一个小小的思考表明:

$$ 0 \ le r


$ p如果$ d_b = 2 s_b $,则为/ 3 $。


$$
\开始{aligned}
g&= d_a-p \\
h&= p-s_a \\
\ end {aligned}
$$

将它们插入可能已经翻倍的$ D $公式。

$$
\ begin {aligned}
D 2 ^ k&=(p + g)(ph)-(p + r)(pr)\\
&= [p ^ 2 +(gh )p-gh]-[p ^ 2-r ^ 2] \\
&=(gh)p + [r ^ 2- gh] \\
\ end {aligned}
$$

如果$ g = h $则很简单:如果$ r = g $相等。如果$ r> g $则1较大,否则2较大。


$$
\ begin {aligned}
v_g&= g-r \\
v_h&= h-r \\
\ end {aligned}

求值$ D2 ^ k的RHS的两个项$ equation。

$$
\ begin {aligned}
r ^ 2-gh&= r ^ 2-(r + v_g)(r + v_h)\\
&= -v_g v_h-(v_g + v_h)r \\
\ end {aligned}
$$

and

$$ g- h = v_g-v_h $$

插入它们。

$$
\ begin {aligned}
D 2 ^ k&=(gh )p + [r ^ 2- gh] \\
&=(v_g-v_h)p-v_g v_h-(v_g + v_h)r \\
&= v_g(pr)-v_h(p + r)-v_g v_h \\
&= v_g s_b-v_h d_b-\ left(\ frac {v_h v_g} {2} + \ frac {v_h v_g} {2} \ right)\\
&= v_g(s_b- \ frac {v_h} {2})-v_h(d_b + \ frac {v_g} {2})\\
\ end {aligned}
$$

将两边都乘以2以除去分数。

$$
\ begin {aligned}
D 2 ^ {k + 1}&= v_g (2s_b-v_h)-v_h(2d_b + v_g)\\
\ end {aligned}
$$

如果$ v_g $或$ v_h $为零,则

同样,如果$ v_g $和$ v_h $具有相反的符号,则D的符号确定也很小。

仍然在最后一个工作sliver ......


非常接近。

theHandledPercent 98.6582716049

theCorrectPercent 100.0

稍​​后再继续.......欢迎任何人为相同的标志盒找到正确的处理逻辑。


前一天,又迈出了一大步。

原始符号确定方程可以这样分解:

$$
\开始{对齐}
D&= d_a s_a-d_b s_b \\
&= \ left(\ sqrt {d_a s_a}-\ sqrt {d_b s_b} \ right)\ left(\ sqrt {d_a s_a} + \ sqrt {d_b s_b} \ right)\\
\ end {aligned}
$$

总和始终为正,因此不会影响D的符号。差异因子是两个几何平均值的差。

几何平均值可以用算术平均值来近似。这是“ alpha max加beta min算法”背后的工作原理。算术平均值也是几何平均值的上限。

由于$ s $值的下限是$ d / 2 $,因此可以为几何平均值建立一个大致的下限,而无需进行大量计算。
&\ ge \ frac {d} {2} \\
\ sqrt {ds}&\ ge \ frac {d} {\ sqrt {2}} \\
&= \ frac {\ frac {d} {\ sqrt {2}}} {{d + s)/ 2} \ cdot \ frac { d + s} {2} \\
&= \ frac {\ sqrt {2}} {1 + s / d} \ cdot \ frac {d + s} {2} \\
& \ ge \ frac {\ sqrt {2}} {1 + 1/2} \ cdot \ frac {d + s} {2} \\
&= \ frac {2} {3} \ sqrt {2 } \ cdot \ frac {d + s} {2} \\
&\约0.9428 \ cdot \ frac {d + s} {2} \\
&> \ frac {15} {16 } \ cdot \ frac {d + s} {2} \\
\ end {aligned}
$$
如果a的算术平均值大于b的算术平均值,则上限b的几何平均值的a小于a的几何平均值的下限,这意味着b必须小于a。反之亦然。

这可以处理许多以前未处理的案例。结果现在是:

theHandledPercent 99.52

theCorrectPercent 100.0

上面的源代码已更新。

那些仍未处理的是“太接近通话”。他们可能需要查找表来解决。请继续关注.....


嘿,丹,

好吧,我会缩短它,但没有一个是多余的。除了第一部分外,这就是使球滚动的原因。因此,最重要的摘要将差不多长。我确实打算写一篇博客文章。这是一个有趣的练习,比我最初想象的要深入得多。

我确实整理了有关Olli坡度边界的旁注。实际上需要执行多少操作。叙述中的数学只是操作的理由。

真正的“赢家”应该是最有效的算法。真正的测试是在同一平台上编程并在此处进行测试的两种方法。现在,我可以告诉你,我的代码(用C语言编写)将仅由于我正在使用整数原型制作而他正在使用它,而他正在使用具有大量昂贵操作的浮点数。

我现在的想法是,我未处理的其余0.5%案例最好使用CORDIC迭代方法进行处理。我将尝试以整数形式实现Olli方法的一种变体,包括他的创造力变化的斜率。 “太接近通话”类别的确应该非常接近。

我同意您的观点,Olli做得很好。我从他那里学到了很多东西。

最后,难道我们都不都是赢家吗?


Dan,

您对CORDIC的信念令人鼓舞。我实现了与Olli有所不同的无损CORDIC,但可能等效。到目前为止,我还没有找到您断言这是最终解决方案的说法。我不会发布代码,因为还应该再进行一次测试来限制它。我将区域限制为四分之一圆(相当于一个完整的圆),并且对它们的评分有所不同。

我建议奥利(Olli)也是这样。

这是我目前的结果:



在0.08%的情况下,假设为零,实际为87.6%。这意味着只有0.01%的答案是错误的,即错误地假定为零。对于那些商(I1 ^ 2 + Q1 ^ 2)/(I2 ^ 2 + Q2 ^ 2)。显示了高和低值。取平方根即可得到实际的幅度比。

大约83%的案件是由主要裁定所捕获的,不需要任何进一步的处理。这样可以节省很多工作。在大约0.7%的案例中需要使用CORDIC。 (在以前的测试中为0.5%。)


If ( (db+sb) & 1 ) > 0 Then
   da =<< 1
   sa =<< 1
   db =<< 1
   sb =<< 1
EndIf


你不能做得更好,而且我敢肯定你做不到任何更快。还是没有太多。我已将所有“ X << = 1”语句更改为“ X + = X”,因为这在8088上速度更快。(Sly咧嘴笑)。

代码将保留在此答案中,已经升级。

我的其他答复中将进行进一步的解释。这个已经足够长了,我不能以比这更好的音符结束它。

评论


$ \ begingroup $
@DanBoschen,让我考虑一下。即使有变动和增加,CORDIC似乎也很昂贵。这是在没有乘法的专用处理器上吗?
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19年12月29日在2:09

$ \ begingroup $
您比以往任何时候都更接近金属。我添加了更多案例以缩小范围,我会继续考虑。有趣的挑战。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在2:17



$ \ begingroup $
可以对其进行重组以提高效率。我已经有,将很快发布。我仍在尝试最后一部分的技巧。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在2:35

$ \ begingroup $
@DanBoschen一张图更容易理解,但我无法尽可能地绘制它们。想象一下在第一个象限中包含(a1,b1)的圆的第一个八分之一。在a1> a2的情况下,这意味着第二个值必须在左侧,排除所有点在右侧。现在,通过(a1,b1)画一条45度的向下倾斜线,代表总和比较。此行下的所有第二个值都小于第一个值。然后我们进入答案的“ CORDIC”部分。经过进一步的思考,在a的比较之后进行b的比较并没有真正的好处。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19-12-29在16:37



$ \ begingroup $
@DanBoschen Matt问了同样的事情。我根据他的回答作了初步比较。我想我现在可能有100%仍在努力。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在17:18

#3 楼

给定两个复数$ z_1 = a_1 + jb_1 $和$ z_2 = a_2 + jb_2 $,您要检查

$$ a_1 ^ 2 + b_1 ^ 2> a_2 ^ 2 + b_2 ^的有效性。 2 \ tag {1} $$

等效于

$$(a_1 + a_2)(a_1-a_2)+(b_1 + b_2)(b_1-b_2 )> 0 \ tag {2} $$

在Cedron Dawg的答案中我也看到了这种不平等,但我不确定在他的代码中如何使用它。

请注意,如果$(2)$左侧的两个术语的符号相等,则可以在不进行任何乘法的情况下检查$(2)$的有效性。如果实部和虚部具有相同的分布,则在所有情况下$ 50 $%都是如此。 -负数而不更改复数的大小。因此,$(2)$中的符号检查简化为检查$ a_1-a_2 $和$ b_1-b_2 $的符号是否相等。显然,如果一个复数的实部和虚部的大小均大于另一个复数的实部和虚部的大小,则可以保证第一个复数的大小大于另一个复数的大小。复数。

如果不基于$(2)$进行乘法运算就无法做出决定,则可以对以下不等式使用相同的技巧:

$$(a_1 + b_2)(a_1-b_2)+(b_1 + a_2)(b_1-a_2)> 0 \ tag {3} $$

,它等同于$(1)$。同样,如果$(3)$左侧的两个术语的符号相等,我们可以不用乘积就可以做出决定。

在仅根据符号检查捕获使用$(2)$的$ 50 $%的案例后,我们根据$(3)$使用符号检查捕获了另外$ 1/6 $(为什么?)的案例。这给我们留下了$ 1/3 $的情况,在这些情况下,我们不能根据等式$(2)$和$(3)$中的简单符号检查来乘而不做决定。对于剩下的这些情况,我没有比使用任何一种已知方法近似一个复数的大小或使用等式更简单的解决方案。 $(2)$或$(3)$与两个必要的乘法。

以下Octave / Matlab代码显示了一个简单的实现,无需使用任何近似值。如果两个复数的实部和虚部具有相同的分布,则所有情况下的$ 2/3 $都可以不加任何乘积地处理,并且在$ 1/3 $的情况下,我们需要两个乘积,即平均每次比较需要$ 0.67 $乘法。 >我对您的代码进行了一些格式化,并添加了一些注释,以便可以将其与我的代码进行比较。

也更改了一些功能。升级您的披萨切片机,应带您达到80%的水平而不会成倍增加。使用旧狗技巧使乘法比较交换意识到了。

Ced

% given: two complex numbers z1 and z2
% result: r=0    |z1| = |z2|
%         r=1    |z1| > |z2|
%         r=2    |z1| < |z2|

a1 = abs(real(z1)); b1 = abs(imag(z1));
a2 = abs(real(z2)); b2 = abs(imag(z2));

if ( a1 < b1 ),
    tmp = a1;
    a1 = b1;
    b1 = tmp;
endif

if ( a2 < b2 ),
    tmp = a2;
    a2 = b2;
    b2 = tmp;
endif

swap = 0;
if ( a2 > a1 )
    swap = 1;
    tmp = a1;
    a1 = a2;
    a2 = tmp;
    tmp = b1;
    b1 = b2;
    b2 = tmp;
endif

if ( b1 > b2 )
    if( swap )
        r = 2;
    else
        r = 1;
    endif
else
    tmp1 = ( a1 + a2 ) * ( a1 - a2 );
    tmp2 = ( b1 + b2 ) * ( b2 - b1 );
    if ( tmp1 == tmp2 )
        r = 0;
    elseif ( tmp1 > tmp2 )
        r = 1;
    else
        r = 2;
    endif
endif


评论


$ \ begingroup $
您也可以交换a和b WLOG。这可以在以后的答案中保存一些比较。不确定,我可能已经塞了这个洞,但是在“需要确定”的情况下,我认为我的$ v_g $和$ v_h $不能有相同的符号。现在,您可能会立即看到它。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在16:42

$ \ begingroup $
@CedronDawg:是的,这就是到达情商的方式。来自等式的$(3)$ $(2)$通过交换两个复数之一的$ a $和$ b $。我的问题是,我仍然有$ 1/3 $的案件无法做出决定。
$ \ endgroup $
– Matt L.
19/12/29在16:45



$ \ begingroup $
那不是我的意思,您只是做了一个不同的关联。我是说您可以假设$ a_1 \ ge b_1 $和$ a_2 \ ge b_2 $ WLOG。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在16:49

$ \ begingroup $
@CedronDawg:是的,我理解。在您的方法中,仍有多少百分比的案件尚未确定?
$ \ endgroup $
– Matt L.
19-12-29在16:52



$ \ begingroup $
如果$ a_1 = b_1 $,则为0%;如果$ b_1 = 0 $,则为一半。 (在我的回答下查看有关Dan的$ a_1> a_2 $情况的图表注释)对于任意$(a_1,b_1)$,绘制垂直线直至$ a = b $线和45度角排列到$ a = b $行。这形成一个三角形,包含一个圆弧。对于斜线以下的第二个值,1较大(它们都在圆内)。对于线内第二个值,线上方的1较大。如果第二点在圆的外面,则它更大,但不能在右边。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/29在17:06

#4 楼

1.避免乘法的对数和指数

要完全避免乘法,可以使用$ \ log $和$ \ exp $表并计算:

$$ I ^ 2 + Q ^ 2 = \ exp \!\ big(2 \ log(I)\ big)+ \ exp \!\ big(2 \ log(Q)\ big)。\ tag {1} $$

由于$ \ log(0)=-\ infty,$,您需要分别检查和计算此类特殊情况。

如果出于某些原因访问$ \ exp $表比访问$ \ log $表贵得多,那么比较平方幅度的对数可能会更有效:

$$ \ begin {eqnarray} I_1 ^ 2 + Q_1 ^ 2& \ lessgtr&I_2 ^ 2 + Q_2 ^ 2 \\
\ Leftrightarrow \ quad \ log(I_1 ^ 2 + Q_1 ^ 2)&\ lessgtr&\ log(I_2 ^ 2 + Q_2 ^ 2),\ end {eqnarray} \ tag {2} $$

每个计算依据(请参阅:高斯对数):

$$ \ log(I ^ 2 + Q ^ 2)= 2 \ log( I)+ \ log \!\ Big(1 + \ exp \!\ big(2 \ log(Q)-2 \ log(I)\ big)\ Big)。\ tag {3} $$

对于以上任何一个公式,您都可以使用$ \ log $和$ \ exp $共享的任何基数,其中基数$ 2 $很容易est表示二进制数字。

要计算$ \ log_2(a)$:

$$ \ log_2(a)= \ operatorname {floor} \!\ big(\ log_2(a)\ big)+ \ log_2 \ left(\ frac {a} {2 ^ {\ displaystyle \ operatorname {floor} \!\\ big(\ log_2(a)\ big)}} \ right),\ tag {4} $$

其中$ \ operatorname {floor} $仅返回其参数的整数部分。可以通过计算$ a $二进制表示形式的前导零并添加取决于所选表示形式的常数来计算第一项。在第二项中,可以通过二进制移位来计算除以2的整数次幂,所得的$ \ log_2 $自变量始终在$ [1,2)$范围内,这很容易列出。 >
类似地,对于$ 2 ^ a $,我们有:

$$ 2 ^ {\ displaystyle a} = 2 ^ {\ displaystyle a-\ operatorname {floor}(a)} \ times 2 ^ {\ displaystyle \ operatorname {floor}(a)}。\ tag {5} $$

可以通过二进制移位来计算乘以2的整数次方。第一个指数始终在$ [0,1)$范围内,这很容易列出。

2。减少乘法次数

基本幅值比较计算中有四个乘法,但是可以通过两种替代方式将其减少为两个乘法:
$$ \ begin {数组} {rrcl}&I_1 ^ 2 + Q_1 ^ 2&\ lesslessr&I_2 ^ 2 + Q_2 ^ 2 \\
\ Leftrightarrow&I_1 ^ 2-I_2 ^ 2&\ lesslessr&Q_2 ^ 2-Q_1 ^ 2 \\
\ Leftrightarrow& (I_1 + I_2)(I_1-I_2)&\ lessgtr&(Q_2 + Q_1)(Q_2-Q_1)\\
\ Leftrightarrow&I_1 ^ 2-Q_2 ^ 2&\ lessgtr&I_2 ^ 2-Q_1 ^ 2 \\
\ Leftrightarrow&(I_1 + Q_2)(I_1-Q_2)&\ lessgtr&(I_2 + Q_1)(I_2-Q_1)。\ end {array} \ tag {6} $$

如果您阅读$ \ Leftrightarrow $等号,那么您也可以将$ \ lessgtr $读作三向比较的“太空飞船算子”(好吧,现在看起来不那么多了。 $ 123 \ lessgtr 456 = -1 $,$ 123 \ lessgtr 123 = 0 $和$ 456 \ lessgtr 123 = 1 $。

也@CedronDawg和@MattL。提出了乘法减少技巧,下面的很多或类似的想法也可以在他们的答案和评论中找到。

3。 CORDIC

CORDIC(坐标旋转数字计算机)算法通过对点绕原点进行近似旋转来工作,每次迭代将旋转角的绝对值减半。这是一种用于幅度比较问题的算法。它无法检测到大小相等,而随机输入的可能性极小,并且在近似相等的情况下,由于算术精度有限,也会给出错误的结果。

让算法从点开始$(I_1 [0],Q_1 [0])$和$(I_2 [0],Q_2 [0])$在第一个八分圆中,这样这些点的大小与输入$(I_1,Q_1)$和$(I_2,Q_2)$:

$$ \ begin {gather}(I_1 [0],Q_1 [0])= \ begin {cases)
(| Q_1 |,| I_1 |)&\ text {if} | I_1 | <| Q_1 |,\\
(| I_1 |,| Q_1 |)&\文本{否则。}
\ end {cases} \\
(I_2 [0],Q_2 [ 0])= \ begin {cases}
(| Q_2 |,| I_2 |)&\ text {if} | I_2 | <| Q_2 |,\\
(| I_2 |,| Q_2 |)&\文本{否则。}
\ end {cases} \ end {gather} \ tag {7} $$

图1.起点分别是$(I_1 [0],Q_1 [0])$(蓝色)和$(I_2 [0],Q_2 [0])$(红色)第一个八位圆(粉红色)。

每个复数的角度由$ 0 \ le \ operatorname {atan2}(Q [n],I [n])\ le \ pi / 4 $限制。 CORDIC伪旋转进一步减小了上限\\ pi / 4 $(请参见图2和4),因此在迭代$ n $时,如果满足以下任何条件,则算法可以以肯定的答案终止:


如果$ I_1 [n] 如果$ I_1 [n]> I_2 [n] $和$ I_1 [n] + Q_1 [n] / 2 ^ n> I_2 [n] + Q_2 [n] / 2 ^ n $,则第一个复数的大小会更大。

在第$ 0 $次迭代中进行任何CORDIC伪旋转之前,已经检查了条件。对于迭代$ n> 0 $,条件是@CedronDawg建议$ n = 0 $的推广(图4)。如果算法没有在确定的答案条件检查后终止,则使用伪旋转继续进行下一次迭代:

$$ \ begin {eqnarray} I_1 [n]&=&I_1 [n- 1] + Q_1 [n-1] / 2 ^ n,\\
Q_1 [n]&=&\ big | Q_1 [n-1]-I_1 [n-1] / 2 ^ n \ big | ,\\
I_2 [n]&=&I_2 [n-1] + Q_2 [n-1] / 2 ^ n,\\
Q_2 [n]&=&\ big | Q_2 [ n-1]-I_2 [n-1] /2^n\big|.\end {eqnarray} \ tag {8} $$

图2. CORDIC算法的第一次迭代:首先将点伪旋转-26.56505117度(大约-22.5度),以使可能的点位置(粉红色)更靠近正实轴。然后,将落在实轴下方的点镜像回到非负$ Q $一侧。 {17} / 4 \ approx 1.030776406 $,并且在连续迭代中接近1的因子。对于幅度比较而言,这不是问题,因为两个复数的幅度总是乘以同一因子。每个连续的回合大约将旋转角度减半。

图3.更复杂的确定答案条件集合2中的第一个条件报告第二个复数的大小大于第一个复数的大小复数位于两条线的左侧,这两条线在第二个复数处相交,并且垂直于复点可能位置(粉红色/紫色)的两个边界。由于CORDIC伪旋转,上边界的斜率为$ 2 ^ {-n} $,因此进行精确的条件检查是可行的。虚线圆所包围的“比萨饼切片”中只有一小部分位于检测区域之外。根据统一的随机抽样,检查会在81%的情况下以答案终止算法。否则,将在第一次CORDIC迭代后重做确定答案条件检查,从而将终止概率提高到94%。经过两次迭代后,概率为95%,经过三次迭代后为98%,以此类推。图3总结了作为最大允许迭代次数的函数的性能。

如果超过了最大迭代次数,则通过比较部分计算的最终伪旋转结果的$ I $分量来做出“不确定”的猜测答案,该伪旋转的结果以可能的点位置为中心围绕实轴:
$$ I_1 [n] + Q_1 [n] / 2 ^ {n + 1} \ lessgtr I_2 [n] + Q_1 [n] / 2 ^ {n + 1}。\ tag {9} $$

图4.使用确定的答案条件集1,在单位圆内均匀且独立分布的$ 10 ^ 7 $个随机点对的算法性能导致错误的答案以及未曾遇到的猜测(不确定答案),错误答案和确定答案的频率。

跳过确定答案条件检查

也可以只运行预定义次数的CORDIC迭代而无需确定答案条件,并在最后进行猜测,每次迭代保存两次比较与简单的肯定答案条件集合1相比,跳过集合2和3中的某些肯定答案条件检查也没有什么害处,因为在以下迭代中也将满足该条件。未使用的想法

我还想出了这个肯定的答案条件集,该条件集看似简单,但实际上更为复杂,因为它不允许重复使用部分计算: >
如果$ I_1 [n] 如果$ I_1 [ n]> I_2 [n] + I_2 [n] / 2 ^ {2n + 1} $,则第一个复数的大小较大。

这里$ I_2 [n]-I_2 [n] / 2 ^ {2n + 1} $很容易计算$ 2 ^ n I_2 [n] / \ sqrt {2 ^ {2n} + 1} $的下限(已计算通过求解$ y = 2 ^ {-n} x,$ $ x ^ 2 + y ^ 2 = I_2 [n] ^ 2] $),这是$ I_1 [n] $的最小上限作为$ I_2的函数[n] $和$ n $当第二个复数的大小较大时。对于低$ N $,这种近似效果不太好。

图5.与图相同。 4,但对于上述替代的肯定答案条件集。

我最初还尝试了该肯定答案条件集,该条件集只是简单地检查了复数之一是否位于另一个的左下方:


如果$ I_1 [n] 如果$ I_1 [n]> I_2 [n] $和$ Q_1 [n]> Q_2 [n] $,则第一个复数的大小较大。

围绕实轴的镜像似乎调整点的$ Q $坐标,以便满足大量复杂对数且迭代次数少的条件。但是,所需的迭代次数通常约为其他确定答案条件集所需迭代次数的两倍。

图6.与图相同。 4和5,但要设置上述肯定的回答条件。对于整数或等效的定点输入和较小的位深度,可以穷举地测试所有输入组合,并遇到有问题的组合,这些组合在无限的输入位深度的限制内变得越来越稀有。

对于具有2s的输入补充一定位深度的实部和虚部,如果我们将数字镜像到第一个八分位数,同时又保留其幅度,则会得到一组复数。在该集合中,某些复数与该集合中的其他复数具有相同的大小(图7)。 CORDIC算法可能无法确定此类数字的大小相等。来自连续概率分布的成对的实复数具有相等大小的零概率。如果效率很重要,并且算法的输入被实数量化为整数,那么有意义的是,还允许幅度比较算法返回小于等于量化步长或量化步长一半之类的差的假不等式。 。

图7.整数$ I $和$ Q $分量小于或等于2 ^ 7的第一个八分位数复数,由来自同一集合的具有相同幅度的复数计数着色。浅灰色:唯一的幅度,较深:更复杂的数字具有相同的幅度。以红色突出显示的是相同数量的复数的最大子集之一,在本例中为$ \ sqrt {5525} $。任何大小的子集的大小很少是整数。

整数实现应从输入向左移动开始,以定点表示形式给出几个小数部分位(图8)。对于迭代的$ I_1 [n] $,$ Q_1 [n],$,$ I_2 [n] $,$ Q_2 [n] $组件,该实现还将需要整数部分的额外空间。某些比较计算中的加法结果可能需要再增加一位空间。 2的幂除法是通过右移完成的。我没有研究过四舍五入的右移可能会减少小数位的需求。误差达到输入量化步长一半的最大迭代次数(可能会为较小的幅值差给出错误的答案)也得到了广泛的测试(图8),但不能保证所有最坏的情况都将得到覆盖。

图8.输入位深度$ b $的整数实现参数,当要求算法返回大于大小为输入数字精度一半的幅度差的正确答案时。 >
另一个未使用的想法

可以使用前导零的计数,这等效于$ \ operatorname {floor} \!\ big(\!\ operatorname {log2}(x )\ big)$,再结合其他二进制操作,确定算法是否可以直接向前跳转到以后的较小CORDIC伪旋转(图9)。伪代码:

if (Q > 0) {
  diff = clz(Q) - clz(I);
  n = diff;
  if (I >= Q << diff) n++;
  if (I >= Q << (diff + 1)) n++;
  // Start at iteration n.
} else {
  // No need to iterate as we are on the real line.
}


需要为两个复数选择较小的n,因为由于迭代而无法分别伪旋转复数-依赖的幅度倍增因子。该技巧可以重复多次。最后,我认为这个技巧对于当前的问题来说太复杂了,但也许可以在其他地方找到用处。

图9.二进制技巧的输出,以确定复数所需的CORDIC伪旋转(请参见最后的源代码)。对于一对复数,需要选择较大的旋转。

C ++清单:浮点CORDIC幅值比较算法和测试

// -*- compile-command: "g++ --std=c++11 -O3 cordic.cpp -o cordic" -*-
#include <random>
#include <algorithm>
#include <chrono>
#include <functional>
#include <stdio.h>

std::default_random_engine gen(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<double> uni(-1.0, 1.0);

// Returns sign of I1^2 + Q1^2 - (I2^2 + Q2^2). The sign is one of -1, 0, 1.
// sure is set to true if the answer is certain, false if there is uncertainty
using magnitude_compare = std::function<int(double I1, double Q1, double I2, double Q2, int maxIterations, bool &sure)>;

int magnitude_compare_reference(double I1, double Q1, double I2, double Q2) {
  double mag1 = I1*I1 + Q1*Q1;
  double mag2 = I2*I2 + Q2*Q2;
  if (mag1 < mag2) {
    return -1;
  } else if (mag1 > mag2) {
    return 1;
  } else {
    return 0;
  }
}

// This algorithm never detects equal magnitude
int magnitude_compare_cordic_olli(double I1, double Q1, double I2, double Q2, int maxIterations, bool &sure) {
  I1 = fabs(I1);
  Q1 = fabs(Q1);
  I2 = fabs(I2);
  Q2 = fabs(Q2);
  if (Q1 > I1) std::swap(I1, Q1);
  if (Q2 > I2) std::swap(I2, Q2);
  sure = true;
  // if (I1 < I2 && Q1 < Q2) { // Set 1
  if (I1 < I2 && I1 + Q1 < I2 + Q2) { // Set 2 / @CedronDawg
  // (I1 < I2 - I2/2) { // Set 3
    return -1;
  }
  // if (I1 > I2 && Q1 > Q2) { // Set 1
  if (I1 > I2 && I1 + Q1 > I2 + Q2) { // Set 2 / @CedronDawg
  // if (I1 > I2 + I2/2) { // Set 3
    return 1;
  }
  int n;
  for (n = 1; n <= maxIterations; n++) {
    double newI1 = I1 + Q1*pow(2, -n);
    double newQ1 = Q1 - I1*pow(2, -n);
    double newI2 = I2 + Q2*pow(2, -n);
    double newQ2 = Q2 - I2*pow(2, -n);
    I1 = newI1;
    Q1 = fabs(newQ1);
    I2 = newI2;
    Q2 = fabs(newQ2);
    // if (I1 < I2 && Q1 < Q2) { // Set 1
    if (I1 < I2 && I1 + Q1*pow(2, -n) < I2 + Q2*pow(2, -n)) { // Set 2
    // if (I1 < I2 - I2*pow(2, -2*n - 1)) { // Set 3
      return -1;
    }
    // if (I1 > I2 && Q1 > Q2) { // Set 1
    if (I2 < I1 && I2 + Q2*pow(2, -n) < I1 + Q1*pow(2, -n)) { // Set 2
    // if (I2 < I1 - I1*pow(2, -2*n - 1)) { // Set 3
      return 1;
    }
  }
  n--;
  sure = false;
  if (I1 + Q1*pow(2, -n - 1) < I2 + Q2*pow(2, -n - 1)) {
    return -1;
  } else {
    return 1;
  }
}

void test(magnitude_compare algorithm, int maxIterations, int numSamples) {
  int numSure = 0;
  int numWrong = 0;
  int numSureWrong = 0;
  double maxFailedMagDiff = 0;
  for (int sample = 0; sample < numSamples; sample++) {
    // Random points within the unit circle
    double I1, Q1, I2, Q2;
    do {
      I1 = uni(gen);
      Q1 = uni(gen);
    } while (I1*I1 + Q1*Q1 > 1);
    do {
      I2 = uni(gen);
      Q2 = uni(gen);
    } while (I2*I2 + Q2*Q2 > 1);
    bool sure;
    int result = algorithm(I1, Q1, I2, Q2, maxIterations, sure);
    int referenceResult = magnitude_compare_reference(I1, Q1, I2, Q2);
    if (sure) {
      numSure++;
      if (result != referenceResult) {
        numSureWrong++;
      }
    }
    if (result != referenceResult) {
      numWrong++;
      double magDiff = fabs(sqrt(I1*I1 + Q1*Q1) - sqrt(I2*I2 + Q2*Q2));
      if (magDiff > maxFailedMagDiff) {
        maxFailedMagDiff = magDiff;
      }
    }
  }
  printf("%d,", maxIterations);  
  printf("%.7f,", (numSamples-numSure)/(double)numSamples);  
  printf("%.7f,", numWrong/(double)numSamples);  
  printf("%.7f,", numSureWrong/(double)numSamples);  
  printf("%.10f\n", maxFailedMagDiff);  
}

int main() {
  int numSamples = 10000000;
  printf("Algorithm: CORDIC @OlliNiemitalo\n");
  printf("max iterations,frequency unsure answer,frequency wrong answer,frequency sure answer is wrong,max magnitude difference for wrong answer\n");
  for (int maxIterations = 0; maxIterations < 17; maxIterations++) {
    test(*magnitude_compare_cordic_olli, maxIterations, numSamples);
  }
  return 0;
}


无花果的p5.js清单。 7&8

该程序可以在editor.p5js.org中运行,并根据未注释的部分绘制图7或8。

function setup() {
  let stride = 4;
  let labelStride = 8;
  let leftMargin = 30;
  let rightMargin = 20;
  let bottomMargin = 20;
  let topMargin = 30;
  let maxInt = 128;
  let canvasWidth = leftMargin+maxInt*stride+rightMargin;
  let canvasHeight = topMargin+maxInt*stride+bottomMargin;
  createCanvas(canvasWidth, canvasHeight);
  background(255);
  textAlign(LEFT, CENTER);
  text('Q', leftMargin+stride, topMargin+labelStride*stride/2)
  textAlign(CENTER, CENTER);
  text('I', canvasWidth-rightMargin/2, canvasHeight-bottomMargin)
  textAlign(RIGHT, CENTER);
  for (let Q = 0; Q <= maxInt; Q += labelStride) {
    text(str(Q), leftMargin-stride, canvasHeight-bottomMargin-Q*stride);
    line(leftMargin, canvasHeight-bottomMargin-Q*stride, canvasWidth-rightMargin, canvasHeight-bottomMargin-Q*stride);
  }
  textAlign(CENTER, TOP);
  for (let I = 0; I <= maxInt; I += labelStride) {
    text(str(I), leftMargin + I*stride, canvasHeight-bottomMargin+stride);
    line(leftMargin+I*stride, topMargin, leftMargin+I*stride, canvasHeight-bottomMargin);
  }    
  let counts = new Array(maxInt*maxInt*2+1).fill(0);
  let maxCount = 0;
  let peakSq = 0;
  for (let Q = 0; Q <= maxInt; Q++) {
    for (let I = Q; I <= maxInt; I++) {
      counts[I*I + Q*Q]++;
      if (counts[I*I + Q*Q] > maxCount) {
        maxCount = counts[I*I + Q*Q];
        peakSq = I*I + Q*Q;
      }
    }
  }
  for (let Q = 0; Q <= maxInt; Q++) {
    for (let I = Q; I <= maxInt; I++) {
      strokeWeight(stride-1);

      // Plot 7
      if (I*I + Q*Q == peakSq) {
        strokeWeight(stride+1);
        stroke(255,0,0);
      } else {
        stroke(255-32-(255-32)*(counts[I*I + Q*Q] - 1)/(maxCount - 1));
      }

/*      // Plot 8      
      let diff = Math.clz32(Q) - Math.clz32(I);
      let iter = diff + (I >= Q << diff) + (I >= Q << diff + 1);
      if (Q) stroke(255-iter*32); else stroke(0); */

      point(leftMargin + I*stride, canvasHeight-bottomMargin-Q*stride);
    }
  }
}


C ++列表:整数输入的CORDIC算法

// -*- compile-command: "g++ --std=c++11 -O3 intcordic.cpp -o intcordic" -*-
#include <algorithm>
#include <cstdint>
#include <cstdlib>

const int maxIterations[33] = {
  0, 0, 0, 0, 1, 2, 3, 3,
  4, 5, 5, 6, 7, 7, 8, 8,
  8, 9, 9, 10, 10, 11, 11, 12,
  12, 13, 13, 14, 14, 15, -1, -1, // -1 is a placeholder
  -1
};

const int numFractionalBits[33] = {
  0, 0, 1, 2, 2, 2, 2, 3,
  3, 3, 3, 3, 3, 3, 3, 4,
  4, 4, 4, 4, 4, 4, 4, 4,
  4, 4, 4, 4, 4, 5, -1, -1, // -1 is a placeholder
  -1
};

struct MagnitudeCompareResult {
  int cmp; // One of: -1, 0, 1
  double cost; // For now: number of iterations
};

MagnitudeCompareResult magnitude_compare_cordic_olli(int32_t I1, int32_t Q1, int32_t I2, int32_t Q2, int b) {
  double cost = 0;
  int n = 0;
  int64_t i1 = abs((int64_t)I1) << numFractionalBits[b];
  int64_t q1 = abs((int64_t)Q1) << numFractionalBits[b];
  int64_t i2 = abs((int64_t)I2) << numFractionalBits[b];
  int64_t q2 = abs((int64_t)Q2) << numFractionalBits[b];
  if (q1 > i1) {
    std::swap(i1, q1);
  }
  if (q2 > i2) {
    std::swap(i2, q2);
  }
  if (i1 < i2 && i1 + q1 < i2 + q2) {
    return {-1, cost};
  }
  if (i1 > i2 && i1 + q1 > i2 + q2) {
    return {1, cost};
  }  
  for (n = 1; n <= maxIterations[b]; n++) {
    cost++;
    int64_t newi1 = i1 + (q1>>n);
    int64_t newq1 = q1 - (i1>>n);
    int64_t newi2 = i2 + (q2>>n);
    int64_t newq2 = q2 - (i2>>n);
    i1 = newi1;
    q1 = abs(newq1);
    i2 = newi2;
    q2 = abs(newq2);
    if (i1 < i2 && i1 + (q1>>n) < i2 + (q2>>n)) {
      return {-1, cost};
    }
    if (i2 < i1 && i2 + (q2>>n) < i1 + (q1>>n)) {
      return {1, cost};
    }
  }
  if (i1 + (q1>>(n + 1)) < i2 + (q2>>(n + 1))) {
    return {-1, cost};
  } else {
    return {1, cost};
  }
}


评论


$ \ begingroup $
当然可以。 “效率”是标题的一部分。我正在使用的总和测试取代您的“ I1 $ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/30在13:10

$ \ begingroup $
您的意思是,随着您的最右边的点接近实轴,-1,-2,-4等,线应变得更陡。线在圆上切出一条割线。一个点是您的最右边的点,理想的另一个点是您可以保证另一个点在下面的角度的交点。当最右边的点接近I = Q线(最初保证两个点都在下面)时,将达到-1的斜率。恐怕计算坡度比我们要解决的问题要复杂得多。您可能会有一个可行的估计。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
19/12/30在18:43

$ \ begingroup $
@OlliNiemitalo不错的工作Olli,我正在使用拟议的排名方法来更新我的问题,我想在潜入之前先辩论/达成协议。
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月1日在22:05

$ \ begingroup $
@DanBoschen我在我的第一个答案中所包含的测试程序中添加了Olli算法的一个版本。它应该为Dan节省一些工作,但是请Olli检查它的准确性。不要开枪!
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
20年1月2日,0:54

$ \ begingroup $
@OlliNiemitalo请参阅我对计分建议的更新---将其留待几天辩论。
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月2日,下午3:33

#5 楼

我将其作为单独的答案,因为我的其他答案已经太长了,这是一个独立的主题,但仍与OP问题非常相关。请从另一个答案开始。

人们对最初的“提前”测试的有效性大惊小怪,我称之为主要判定。

基本方法如下:

If x1 > x2 Then
   If y1 > y2 Then


割线方法如下:

If x1 > x2 Then
   If x1 + y1 >= x2 + y2 Then


[非常重要的编辑:对角线上的点也位于“比萨饼切片”上,因此在总和比较中应使用等号。在精确的整数情况下,这变得很重要。]

那么,两个额外的和会给您带来什么呢?事实证明很多。

首先是简单方法。想象一下第一个象限的下半部分(x = y线下方)的一个点(x,y)。即$ x \ ge 0 $,$ y \ ge 0 $和$ x \ ge y $。让它代表最右边的一点,而不失一般性。然后,另一点必须落在该点或该点的左侧,或落在通过绘制一条垂直线穿过(x,y)直至对角线而形成的三角形内。则三角形的面积为:

$$ A_ {full} = \ frac {1} {2} x ^ 2 $$

基本方法将成功通过(x,y)的水平线下方的完整三角形中的所有点。该区域的面积为:

$$ A_ {base} = xy-\ frac {1} {2} y ^ 2 $$

这一点可以定义为:

$$ p(x,y)= \ frac {A_ {base}} {A_ {full}} = \ frac {xy-\ frac {1} { 2} y ^ 2} {\ frac {1} {2} x ^ 2} = 2 \ frac {y} {x}-\ left(\ frac {y} {x} \ right)^ 2 $$

快速检查表明,如果点在x轴上,则概率为零;而如果点在对角线上,则概率为1。

这也可以容易用极坐标表示。设$ \ tan(\ theta)= y / x $。

$$ p(\ theta)= 2 \ tan(\ theta)-\ tan ^ 2(\ theta)$$

同样,$ p(0)= 0 $和$ p(\ pi / 4)= 1 $

要评估平均值,我们需要知道采样区域的形状。如果是正方形,那么我们可以从一条垂直迹线计算平均值,而不会失去一般性。同样,如果它是圆形的,则可以从一条圆弧轨迹中计算出平均值。 /> $$
\开始{aligned}
\ bar p&= \ frac {1} {1} \ int_0 ^ 1 p(1,y)dy \\
&= \ int_0 ^ 1 2y-y ^ 2 dy \\
&= \ left [y ^ 2-\ frac {1} {3} y ^ 3 \ right] _0 ^ 1 \\
&= \左[1-\ frac {1} {3} \ right]-[0-0] \\
&= \ frac {2} {3} \\
&\约0.67
\ end {aligned}
$$

圆形保护套要坚固一些。

$$
\ begin {aligned}
\ bar p&= \ frac {1} {\ pi / 4} \ int_0 ^ {\ pi / 4} p(\ theta)d \ theta \\
&= \ frac {1} {\ pi / 4} \ int_0 ^ {\ pi / 4} 2 \ tan(\ theta)-\ tan ^ 2(\ theta)d \ theta \\
&= \ frac {1} {\ pi / 4} \ int_0 ^ {\ pi / 4} 2 \ frac {\ sin(\ theta)} {\ cos(\ theta)}-\ frac {\ sin ^ 2(\ theta)} {\ cos ^ 2(\ theta) } d \ theta \\
&= \ frac {1} {\ pi / 4} \ int_0 ^ {\ pi / 4} 2 \ frac {\ sin(\ theta)} {\ cos(\ theta) }-\ frac {1- \ cos ^ 2(\ theta)} {\ cos ^ 2(\ theta)} d \ theta \\
&= \ frac {1} {\ pi / 4} \ int_0 ^ {\ pi / 4} 2 \ frac {\ si n(\ theta)} {\ cos(\ theta)}-\ frac {1} {\ cos ^ 2(\ theta)} + 1 \; d \ theta \\
&= \ frac {1} {\ pi / 4} \ left [-2 \ ln(\ cos(\ theta))-\ tan(\ theta)+ \ theta \ right] _0 ^ {\ pi / 4} \\
&= \ frac {1} {\ pi / 4} \ left [\ left(-2 \ ln(\ cos(\ pi / 4))-\ tan (\ pi / 4)+ \ pi / 4 \ right)-(0-0 + 0)\ right] \\
&= \ frac {1} {\ pi / 4} \ left(\ ln( 2)-1 + \ pi / 4 \ right)\\
&\大约0.61
\ end {aligned}
$$

将其与割线比较方法。从(x,y)到原点画一条线。这形成了下三角形。现在绘制一条斜率为-1的直线,直到对角线为止。这形成了上部三角形。

$$ A_ {lower} = \ frac {1} {2} xy $$

$$ A_ {upper} = \ frac { 1} {4}(x ^ 2-y ^ 2)$$

现在,概率定义为:

$$p(x,y)&= \ frac {A_ {lower} + A_ {upper}} {A_ {full}} \\
&= \ frac {\ frac {1} {2} xy + \ frac {1} {4}(x ^ 2-y ^ 2)} {\ frac {1 } {2} x ^ 2} \\
&= \ frac {1} {2} + \ frac {y} {x}-\ frac {1} {2} \ left(\ frac {y} {x} \ right)^ 2
\ end {aligned}
$$

与上述相同的健全性检查将产生预期范围的一半到一半。请注意,对于圆形情况,它也可以像以前一样放入极性形式。

现在可以像上面那样计算方形情况的平均概率。

$$
\ begin {aligned}
\ bar p&= \ frac {1} {1} \ int_0 ^ 1 p(1,y)dy \\
&= \ int_0 ^ 1 \ frac {1} {2} + y-\ frac {1} {2} y ^ 2 dy \\
&= \ left [\ frac {1} {2} y + \ frac {1} {2} y ^ 2-\ frac {1} {6} y ^ 3 \ right] _0 ^ 1 \\
&= \ left [\ frac {1} {2} + \ frac {1} {2}- \ frac {1} {6} \ right]-[0 + 0-0] \\
&= \ frac {5} {6} \\
&\约0.83
\ end {aligned}
$$

有些人可能会这样看,并说:“大不了,您发现的案件可能增加了20%。”从另一角度来看,您将需要进一步处理的案例数量减少了一半。这是两个交易的讨价还价。

极性表壳的方形版本留给读者练习。玩得开心。

[敏锐的读者可能会说,“嗯... 1/2 + 0.61 / 2大约是0.8 ish。有什么大不了?”]

我将在一段时间内解释我的无损CORDIC ...


在我的实现中,直到其他测试用尽后,才会调用end CORDIC例程。 (可以在其他答案中找到有效的Python代码。)这可以将需要处理的案例减少到少于1%。但是,这并不是借口变得懒惰而发狂的借口。

由于这是麻烦的情况,因此可以放心地假设两个量值大致相等或相等。实际上,使用小整数参数,等于情况最普遍。

Olli的方法的目标以及Dan阐明的目标是,使用CORDIC将点向下旋转到实轴,以便可以在单个维度上进行比较。这不是必需的。只需将点对准同一角度即可,使早先失败的简单确定测试更有可能成功。为了实现这一点,希望旋转这些点,以使一个点以相同的角度落在轴的下方,而另一个点在轴的上方。然后,当镜面反射完成时,两个点将在轴上方以相同角度对齐。

因为幅度接近相等,所以这与旋转相同,因此两个点的距离相同旋转后位于轴的上方和下方。定义它的另一种方法是说两个点的中点应尽可能地靠近轴。

不执行任何截断也很重要。旋转必须无损,否则可能导致错误的结果。这限制了我们可以执行的旋转类型。

如何做到?

对于每个点,都会计算出旋转臂值。实际上,在矢量方面更容易理解,因为矢量加法和复数加法在数学上是等效的。对于每个点(作为矢量),将创建一个正交矢量,该矢量的长度相同且指向向下。当将此向量添加到点向量时,由于两个点都在I = Q对角线之下,因此保证了两个点的结果都落在轴的下方。我们想要做的就是将这些向量缩短到恰好合适的长度,以使一个点在轴上,而另一个点在同一距离下。但是,缩短向量通常不能不加截断。

那么什么是花招?而是延长点向量。只要按比例完成,就不会影响结果。使用的度量是中点到轴的距离。这将被最小化。由于目标为零,因此距离是中点的绝对值。通过简单地最小化虚部之和的绝对值,可以节省除法(或移位)。弄清楚在每个步骤中旋转点将垂直终止的位置(无需进行水平计算)。通过将步长矢量添加到点矢量来进行下一步。再次测量该值。一旦开始增加,您便找到了最小值并完成了搜索。

应用旋转。翻转镜子中的以下点。做一个比较。在大多数情况下,只需要旋转一次即可。令人高兴的是,平等案件立即被抓住。

如何消除暴力搜索?这是下一个技巧。而不是在每个步骤中都添加步骤向量,而是将点向量加倍。是的,这是经典的O(log2)改进。一旦价值开始增加,您就知道您已经达到了可能性范围的尽头。在此过程中,您可以巧妙地保存点向量。现在,它们可以用作您阶跃向量的两倍的幂。是的,只需从距离您范围一半的最后一个点开始就可以了。选择下一个最大的步长向量,并看向任一侧。如果找到较小的值,请移至该值。选择下一个最大的步长向量。重复直到您到达单位步长向量。现在,您将拥有与通过残酷搜索找到的单位倍数相同的单位。

请注意:如果两个点的角度确实很浅,则可能需要很多倍数,直到旋转的点跨越轴为止。可能溢出。如果可能的话,在这里使用长整数可能是明智的。有一个hack溢出检查,但这值得进一步调查。在其他情况下,这是一个“理想情况”,因此,在发生这种情况时,应该有一种可以应用的替代检查。可能会采用Olli使用更陡峭的截止线的想法。

仍在努力。.......


我目前正在开发和测试小角度解决方案。请耐心等待...

#6 楼

Sigma Delta Argument Test

我想出了自己的解决方案,其前提是通过测试两个向量的和与差之间的正交角来解决最大向量幅值(包括相等性): />
对于总和$ \ Sigma $以及$ z_1 $和$ z_2 $的差$ \ Delta $表示为(这是2点DFT)

$ \ Sigma = z_1 + z_2 $

$ \ Delta = z_1-z_2 $

$ z_1 $和$ z_2 $之间的角度$ \ phi $(由复数的自变量给出) $ \ Sigma $和$ \ Delta $的共轭乘积:$ arg(\ Sigma \ cdot \ Delta ^ *)$)具有以下属性(请参阅此答案底部的推导):

$ z_1
对于$ z_1 = z_2,| \ phi | = \ pi / 2 $

对于$ z_1> z_2,| \ phi | > \ pi / 2 $

鉴于$ \ pi / 2 $边界的便利性,我们无需计算参数!

这种方法的重要性在于,将恒定半径的极坐标阈值转换为直角坐标阈值,作为一条直线穿过原点,从而提供了一种没有截断错误的更简单算法。 />
这种方法的效率归结为计算两个向量的和与差,然后能够有效地测试它们之间的相位是否大于或小于$ \ pi / 2 $。

如果允许乘数,则可以通过评估复共轭结果的实部很容易解决,因此,如果首先使用乘数来引入完整算法,然后满足问题的目的,没有乘数的方法如下。


如果可以使用乘数

首先介绍允许乘数的基线算法:

1)步骤1:求和$ z_1 = I_1 + jQ_1 $,$ z_2 = I_2 + jQ_2 $:

$ \ Sigma = I _ {\ Sigma} + jQ _ {\ Sigma} =(I_1 + I_2 )+ j(Q_1 + Q_2)$

$ \ Delta = I _ {\ Delta} + jQ _ {\ Delta} =(I_1-I_2)+ j(Q_1-Q_2)$

2)步骤2:计算复合共轭乘积的实数:$ \ Sigma \ cdot \ Delta ^ * $。这是点积,结果的MSB(符号位)直接是二进制答案!

$ q = I _ {\ Sigma} I _ {\ Delta} + Q _ {\ Sigma} Q_ {\ Delta} $

3)第3步:对于三元结果测试q:

$ q <0 \ rightarrow z_2> z_1 $

$ q = 0 \ rightarrow z_2 = z_1 $

$ q> 0 \ rightarrow z_2
所以此方法提供了2实数的二进制>或<结果与4个实数乘法器和3个读加法相比,与平方幅度相比,节省了很多。它本身并不引人注目,因为方程很相似,可以直接实现类似的数学简化(正如@ Cedron,@ MattL,@ Olli在其答案中已经指出的那样),但包括在内以表明其与Sigma Delta的关系参数测试:以类似形式直接进行幅度测试是比较$ I ^ 2 + Q ^ 2 $:

$$ q =(I_1I_1 + Q_1Q_1)-(I_2I_2 + Q_2Q_2)$$

可以将其重写为以下形式以减少乘数(或重新排序以直接匹配上面的方程): I_2 + Q_1)(I_2-Q_1)$$


无乘法器解决方案

无乘法器解决方案通过有效地确定任意复数的位置来完成点在由与原点相交的直线一分为二的平面上。使用这种方法,可以简化目标的确定点,确定该点是在线的上方还是左侧,线的下方或右侧还是线上。

该测试可以通过以下方式可视化:将$ \ Delta $旋转-$ \ pi / 2 $弧度($ \ Delta / j $),然后将$ \ Sigma $和$ \ Delta / j $之间的边界的检验更改为$ 0 $和$ \ pi $。通过交换I和Q,然后更改I上的符号来完成此轮换:$ -j(I + jQ)= Q-jI $这可以简单地合并到$ \ Delta $的修改方程中,这样就无需进一步处理需要:

$$ \ Delta / j =(Q_1-Q_2)+ j(I_2-I_1)$$

在这种情况下,测试是查看$ \ Delta给定的点$位于线y = mx上方,其中m是$ \ Sigma $的虚部与实项之比。 (其中y是由Q表示的虚轴,x是由I表示的实轴)。

用Q1到Q4表示的四个象限对测试是旋转不变的,因此我将Q1称为象限$ \ Sigma $,如下图所示。 Q2和Q4无关紧要,如果$ \ Delta / j $在这两个象限中的任何一个象限中,则可以轻松做出决策。当$ \ Delta / j $在Q3中时,测试为$ \ Delta / j $在Q1中时的负数,因此该算法现在是确定$ \ Delta / j $是否在以上的最有效方法当$ \ Delta / j $和$ \ Sigma $都在Q1中时,y = mx虚线,在虚线下方或在虚线上。



用于有效确定$ \ Delta / j $是高于还是低于穿过原点的线和$ \ Sigma $的线的方法如下:考虑从$ Z_s = \ Sigma $开始,作为$ Z_d = \ Delta / j $。

在计算$ Z_s $和$ Z_d $之前,通过获取$ I_1 $,$ I_2 $,$ Q_1 $和$ Q_2 $的绝对值来强制$ Z_s $处于Q1。

如果$ Z_d $在第3季度,则通过取反将其移至Q1:$ Z_d = \ Delta / j $。这将导致它落在测试线的另一侧,因此设置了一个标志来反转输出解决方案。

如果$ Z_d $在Q2或Q4中,则确定是明确的:如果在Q2中,$ Z_d $必须始终在行上方,因此$ | z_1 | <| z_2 | $。如果在第4季度,$ Z_d $必须始终在该行下方,则$ | z_1 |> | z_2 | $。

如果$ Z_d $在Q3中,则只有在$ Z_d $在新的Q2或Q4中时才能解决,方法是将原点移动到$ Z_s $。这是通过位移增加$ Z_d $直到超出$ I_s $或$ Q_s $边界来实现的。这样可确保$ 2 ^ n $快速增长,并且结果不会超过$ 2Q_s $或$ 2I_s $。减去$ Z_s $并重复测试。通过减去$ Z_s $,由$ Z_d'= Z_d-Z_s $给出的新矢量将朝着Q轴或I轴旋转(也以速率$ 2 ^ n $),最终降落在Q2或Q4再次长大并减去$ I_s $。

该算法的示例Python代码

def CompareMag(I1, Q1, I2, Q2, b = 16):
    '''        
    Given Z1 = I1 + jQ1, Z2 = I2 + jQ2
    I1, I2, Q1, Q2 are b-bit signed integers
    returns: 
       -2: |Z1| < |Z2|
        0: |Z1| = |Z2|
       +2: |Z1| > |Z2|
    '''

    iters = b+2                         # max iterations
    inv = 0                             # Initiate XOR toggle of output

    #---- ensure Zs is in Q1
    I1 = abs(I1); Q1 = abs(Q1)
    I2 = abs(I2); Q2 = abs(Q2)

    #----
    # For speed boost insert optional PD algo here
    #----

    #---- sum and difference   Zs = Is + jQs, Zd = Id + jQd
    Is = I1 + I2; Qs = Q1 + Q2
    Id = Q1 - Q2; Qd = I2 - I1          # rotate Zd by -j


    #---- if Zd is in Q3, invert Zd and invert result
    if Id < 0 and Qd <= 0:
        Id, Qd = -Id, -Qd
        inv = -4                        # reverse output +2 -> -2 or -2 -> +2

    while iters>0:
        #---- Can resolve if Zd is in Q2, Q4 or origin, otherwise iterate
        if Id < 0:
            return inv * -1             # Qd >= Qs so |Z1| < |Z2|
        if Qd < 0:
            return inv * 1              # Id >= Is so |Z1| > |Z2|
        if Id == 0 and Qd == 0:
            return 0                    # |Z1| = |Z2|

        while Id < Is and Qd < Qs:      # grow Zd until Id > Is or Qd > Qs 
            Id <<= 1; Qd <<= 1

        Id = Id - Is; Qd = Qd - Qs      # move origin to Zs

        iters -= 1
    return 0                            # not rotating, so |Z1| = |Z2|


Speed Boost

Cedron的主要确定算法(具有类似的变体)马特(Matt)和奥利(Olli)的代码)通过在进行求和与差计算之前解决大多数情况(高达90%),大大提高了速度。如果我们在不同的机器上得到不同的结果(统计上都非常接近),则需要进一步详细分析以解决此变体是否最快的问题。数学推导

以下是求和与差如何导致角度测试并提供更详细的数学关系(以帮助进行灵敏度测试等)的推导:

考虑

$$ z_1 = A_1e ^ {j \ phi_1} $$
$$ z_2 = A_2e ^ {j \ phi_2} $$

其中$ A_1 $和$ A_2 $是正实数,代表$ z_1 $和$ z_2 $的大小,而$ \ phi_1 $和$ \ phi_2 $是弧度的相位。

将$ z_1 $除以相对于$ z_1 $有表达式$ z_2 $

$$ z_1'= \ frac {z_1} {z_1} = 1 $$
$$ z_2'= \ frac {z_2 } {z_1} = \ frac {A_2} {A_1} e ^ {j(\ phi_2- \ phi_1)} = Ke ^ {j \ phi} $$

如果$ K> 1 $然后$ z_2> z_1 $

$ z_1'$和$ z_2'$的总和和差为:

$$ \ Sig ma = z_1'+ z_2'= 1 + Ke ^ {j \ phi} $$

$$ \ Delta = z_1'-z_2'= 1-Ke ^ {j \ phi} $$

两个向量的复共轭乘法提供了两者之间的角度差;例如:

给出
$$ v_1 = V_1e ^ {j \ theta_1} $$
$$ v_2 = V_2e ^ {j \ theta_2} $$
复共轭积为:
$$ v_1v_2 ^ * = V_1e ^ {j \ theta_1} V_2e ^ {-j \ theta_2} = V_1V_2e ^ {j(\ theta_1- \ theta_2)} $$

因此$ \ Sigma $和$ \ Delta $的复杂共轭乘积与结果$ Ae ^ {j \ theta} $是:

$$
\ begin {对齐}
Ae ^ {j \ theta}&= \ Sigma \ cdot \ Delta ^ * \\
&=(1 + Ke ^ {j \ phi})(1-Ke ^ {j \ phi})^ * \\
&=(1 + Ke ^ {j \ phi})(1-Ke ^ {-j \ phi)})\\
&= 1 + K(2jsin (\ phi))-K ^ 2 \\
&=(1-K ^ 2)+ j2Ksin(\ phi)\\
\ end {aligned}
$$

请注意,当K = 1时,上述内容减少为$ 2jsin(\ phi)$;当K <1时,实数分量始终为正,而当K> 1时,实数分量始终为负,因此: />
为$ K <1,| \ theta | <\ pi / 2 $

对于$ K = 1,| \ theta | = \ pi / 2 $

对于$ K> 1,| \ theta | > \ pi / 2 $

下图显示了快速仿真的结果,以证明上面总结的结果,其中均匀地随机选择了复杂的$ z_1 $,$ z_2 $对,如上图所示:红色和蓝色点,以及由此得出的映射到$ z_1 $和$ z_2 $的和与差之间的角度的映射。


#7 楼

对于我来说,这是前所未有的第三个答案。这是一个全新的答案,因此它不属于其他两个答案。

(有问题的):


max(I,Q)+ min (I,Q)/ 2

Laurent Duval(有疑问的评论):


0.96a + 0.4b

a公民(有疑问的评论):


| a1 | + | b1 | > | a2 | + | b2 |

按照惯例,我将使用$(x,y)$代替$(I,Q)$或$(a,b)$。对于大多数人来说,这可能会使它看起来像是距离度量,而不是复杂的数量级。没关系它们是等效的。我认为这种算法在距离应用中(至少对我而言)比复数评估更有用。

所有这些公式都可以看作是倾斜平面的水平曲线公式。两个输入点中每个输入点的电平都可以用作幅度的代理,并可以直接进行比较。

$$ L(x,y)= ax + by $$

以上三个公式现在可以表示为:

$$
\开始{aligned}
L_ {DB}&= 1.0 x + 0.5 y \\
L_ {LD}&= 0.96 x + 0.4 y \\
L_ {CC}&= 1.0 x + 1.0 y \\
\ end {aligned}
$$

请打鼓。......

最合适的答案(标准到来)是:开始{对齐}
L&\约0.939 x + 0.417 y \\
&\约0.94 x + 0.42 y \\
&\约(15/16)x +(107 / 256)y \\
&= [240 x + 107 y] / 256 \\
&= [(256-16)x +(128-16-4-1)y] / 256 \ \
&= [(x << 8)-(x << 4)\\
&+(y << 7)-(y << 4)-(y << 2)- y] >> 8 \\
\ end {aligned}

这与LD的公式非常匹配。那些老工程师可能使用了计算尺等。或者可能是不同的最佳拟合标准。

但这让我思考。如果查看$ L = 1 $的水平曲线,则这些线试图逼近单位圆。那是突破。如果我们可以将单位圆划分为较小的圆弧,并为每个圆弧找到一条最佳拟合线,则可以找到相应的水平函数并将其用作该圆弧跨度内点的比较器。

我们可以t分隔角很容易,但是我们可以轻松找到$ x = 1 $线的距离。可以通过$ y = mx $定义通过原点的线。这意味着它以$ m $的高度撞到$ x = 1 $线。因此,对于特定的$ m $,如果$ y> mx $在该行之上,则$ y = mx $在该行之上,而$ y
圈成四个弧,{0,1 / 4,1 / 2,3 / 4,1}的值可用于$ m $。通过二进制移位,加法和减法,可以将$ y $与$ mx $进行比较。例如:

$$
\ begin {aligned}
y&<\ frac {3} {4} x \\
4y&<3x \\
(y << 2)&<(x << 1)+ x \\
\ end {aligned}
$$

最佳拟合线段以近似圆弧,也可以进行一些移位,加法和减法来实现。

有关如何做到最好的说明即将出现。

名为“ DanBeastFour”的测试例程使用四个弧。可以通过以下值表判断得出的估计质量:

Deg  Degrees
Rad  Radians
X,Y  Float
x,y  Integer
R    Radius of Integer as Float
r    Returned Estimate as Integer
r/R  Accuracy Metric


Deg Rad      X         Y         x      y       R       r     r/R

 0 0.00  (10000.00,    0.00)  (10000,    0)  10000.00  9921 0.99210 
 1 0.02  ( 9998.48,  174.52)  ( 9998,  175)   9999.53  9943 0.99435 
 2 0.03  ( 9993.91,  348.99)  ( 9994,  349)  10000.09  9962 0.99619 
 3 0.05  ( 9986.30,  523.36)  ( 9986,  523)   9999.69  9977 0.99773 
 4 0.07  ( 9975.64,  697.56)  ( 9976,  698)  10000.39  9990 0.99896 
 5 0.09  ( 9961.95,  871.56)  ( 9962,  872)  10000.09  9999 0.99989 
 6 0.10  ( 9945.22, 1045.28)  ( 9945, 1045)   9999.75 10006 1.00062 
 7 0.12  ( 9925.46, 1218.69)  ( 9925, 1219)   9999.58 10009 1.00094 
 8 0.14  ( 9902.68, 1391.73)  ( 9903, 1392)  10000.35 10010 1.00096 
 9 0.16  ( 9876.88, 1564.34)  ( 9877, 1564)  10000.06 10007 1.00069 
10 0.17  ( 9848.08, 1736.48)  ( 9848, 1736)   9999.84 10001 1.00012 
11 0.19  ( 9816.27, 1908.09)  ( 9816, 1908)   9999.72  9992 0.99923 
12 0.21  ( 9781.48, 2079.12)  ( 9781, 2079)   9999.51  9980 0.99805 
13 0.23  ( 9743.70, 2249.51)  ( 9744, 2250)  10000.40  9966 0.99656 
14 0.24  ( 9702.96, 2419.22)  ( 9703, 2419)   9999.99  9948 0.99480 
15 0.26  ( 9659.26, 2588.19)  ( 9659, 2588)   9999.70  9965 0.99653 
16 0.28  ( 9612.62, 2756.37)  ( 9613, 2756)  10000.27  9981 0.99807 
17 0.30  ( 9563.05, 2923.72)  ( 9563, 2924)  10000.04  9993 0.99930 
18 0.31  ( 9510.57, 3090.17)  ( 9511, 3090)  10000.36 10002 1.00016 
19 0.33  ( 9455.19, 3255.68)  ( 9455, 3256)   9999.93 10008 1.00081 
20 0.35  ( 9396.93, 3420.20)  ( 9397, 3420)  10000.00 10012 1.00120 
21 0.37  ( 9335.80, 3583.68)  ( 9336, 3584)  10000.30 10012 1.00117 
22 0.38  ( 9271.84, 3746.07)  ( 9272, 3746)  10000.12 10009 1.00089 
23 0.40  ( 9205.05, 3907.31)  ( 9205, 3907)   9999.83 10003 1.00032 
24 0.42  ( 9135.45, 4067.37)  ( 9135, 4067)   9999.44  9993 0.99936 
25 0.44  ( 9063.08, 4226.18)  ( 9063, 4226)   9999.85  9982 0.99821 
26 0.45  ( 8987.94, 4383.71)  ( 8988, 4384)  10000.18  9967 0.99668 
27 0.47  ( 8910.07, 4539.90)  ( 8910, 4540)   9999.98  9981 0.99810 
28 0.49  ( 8829.48, 4694.72)  ( 8829, 4695)   9999.71  9994 0.99943 
29 0.51  ( 8746.20, 4848.10)  ( 8746, 4848)   9999.78 10004 1.00042 
30 0.52  ( 8660.25, 5000.00)  ( 8660, 5000)   9999.78 10011 1.00112 
31 0.54  ( 8571.67, 5150.38)  ( 8572, 5150)  10000.08 10015 1.00149 
32 0.56  ( 8480.48, 5299.19)  ( 8480, 5299)   9999.49 10015 1.00155 
33 0.58  ( 8386.71, 5446.39)  ( 8387, 5446)  10000.03 10013 1.00130 
34 0.59  ( 8290.38, 5591.93)  ( 8290, 5592)   9999.73 10008 1.00083 
35 0.61  ( 8191.52, 5735.76)  ( 8192, 5736)  10000.53 10000 0.99995 
36 0.63  ( 8090.17, 5877.85)  ( 8090, 5878)   9999.95  9988 0.99881 
37 0.65  ( 7986.36, 6018.15)  ( 7986, 6018)   9999.63 10001 1.00014 
38 0.66  ( 7880.11, 6156.61)  ( 7880, 6157)  10000.15 10012 1.00118 
39 0.68  ( 7771.46, 6293.20)  ( 7771, 6293)   9999.51 10018 1.00185 
40 0.70  ( 7660.44, 6427.88)  ( 7660, 6428)   9999.74 10023 1.00233 
41 0.72  ( 7547.10, 6560.59)  ( 7547, 6561)  10000.20 10024 1.00238 
42 0.73  ( 7431.45, 6691.31)  ( 7431, 6691)   9999.46 10022 1.00225 
43 0.75  ( 7313.54, 6819.98)  ( 7314, 6820)  10000.35 10018 1.00176 
44 0.77  ( 7193.40, 6946.58)  ( 7193, 6947)  10000.00 10009 1.00090 
45 0.79  ( 7071.07, 7071.07)  ( 7071, 7071)   9999.90  9998 0.99981 


对于野兽来说不是太破旧。是DanBeast_2_8(fka DanBeastFour)的Python代码示例。


        if          yN+yN  <  xN:                           # 2 y < x
           if      (yN<<2) <  xN:                           # 4 y < x
              LN = (xN<<8) -  xN - xN \
                 + (yN<<5) + (yN<<1)
               # = ___ * x + ___ * y                        # y/x = 0.00 to 0.25
           else:
              LN = (xN<<8) - (xN<<4) \
                 + (yN<<6) + (yN<<5) - (yN<<2) - yN - yN
               # = ___ * x + ___ * y                        # y/x = 0.25 to 0.50
        else:
            if     (yN<<2) <  xN + xN + xN:                 # 4 y < 3 x
              LN = (xN<<8) - (xN<<5) - (xN<<2) - xN - xN \
                 + (yN<<7) + (yN<<3) -  yN
               # = 218 * x + 135 * y   (See Table h=8)      # y/x = 0.50 to 0.75 
           else:
              LN = (xN<<7) + (xN<<6) +  xN + xN \
                 + (yN<<7) + (yN<<5) + (yN<<3)
               # = ___ * x + ___ * y                        # y/x = 0.75 to 1.00

        # DN = LN >> 8


看一些数字:


Arc for: y/x = 0.50 to 0.75

Best fit using linear regression: y = -1.615 x + 1.897  

Comparison level function    LN      =  0.851 x + 0.527 y
                             LN_2^8 ~=~   218 x +   135 y  

 h    2^h   a 2^h  a2h    diff diff/2^h     b 2^h  b2h    diff diff/2^h

 0     1    0.851     1 0.1486 0.148647     0.527     1 0.4728 0.472787
 1     2    1.703     2 0.2973 0.148647     1.054     1 0.0544 0.027213
 2     4    3.405     3 0.4054 0.101353     2.109     2 0.1089 0.027213
 3     8    6.811     7 0.1892 0.023647     4.218     4 0.2177 0.027213
 4    16   13.622    14 0.3784 0.023647     8.435     8 0.4354 0.027213
 5    32   27.243    27 0.2433 0.007603    16.871    17 0.1292 0.004037
 6    64   54.487    54 0.4866 0.007603    33.742    34 0.2584 0.004037
 7   128  108.973   109 0.0268 0.000210    67.483    67 0.4832 0.003775
-8-  256  217.946   218 0.0537 0.000210   134.966   135 0.0336 0.000131
 9   512  435.893   436 0.1073 0.000210   269.933   270 0.0671 0.000131


diff / 2 ^ h是单位距离错误。

完成了两个最佳拟合。第一个是最适合圆弧的线段。第二个是比较级别函数的最佳拟合整数表示。试图将一个的精度提高到另一个没有任何意义。由第一个产生的误差是弧的开始和结束角度的函数。 (现在,那应该只是弧长,不是吗?)可以选择秒的误差以匹配任何要求,例如表格。

因此,当您要选择哪个DanBeast适合您的应用程序,您需要提供两个参数d和h。

第一个是if-tree深度(d)。这将定义弧形分区的数量(2 ^ d)和最大精度的界限。在运行时,这会花费额外的if语句。

第二个参数是您希望系数(a,b)的精度(h)有多高。更高的精度会花费更多的班次并在运行时增加。预期每步大约两个班次和两个加/减。在输入变量中,必须至少有(h + 1)位的零余量,以允许移位,加法和减法。加号是符号位清除,YMMY。



很明显,一些老工程师用纸和铅笔敏锐一些,也许还有滑尺或日志表(DIY)。 L.D.提供的方程式在Dan(https://en.wikipedia.org/wiki/Alpha_max_plus_beta_min_algorithm)提供的链接中,最接近最合适的答案。

$ y = mx + c $的线性回归不是最适合使用。这是一种hack。最好的方法是在极坐标中使用最小二乘积分。我现在没有时间。 $ x上的LR =(1 / m)y-(c / m)$会更好地拟合,但仍然很糟糕。由于下一步是最适合的整数,所以没关系。

最佳最佳拟合应该位于每个圆弧的中心。如果您查看上面的数字表,请估计大约在11度处结束的第一个弧,并查找精度度量的峰值和最终值。您不必成为木匠就可以看到那个泡沫不是水平的。现在已经足够近了,但这就是我们进行测试的原因。我将其中一半捐献给不是我的参赛者之一的赛马冠军。现在,奥利(Olli)是默认的获胜者,因为他的例程已经被合并,他有一个答案可以悬赏。


评论Dan的解决方案和一个建议性问题:

和线性代数的观点不同。

$$
\开始{bmatrix}
S \\
D
\ end {bmatrix }
=
\ begin {bmatrix}
1&1 \\
1&-1
\ end {bmatrix}
\ begin {bmatrix}
z_1 \\
z_2
\ end {bmatrix}
=
\ sqrt {2}
\ begin {bmatrix}
\ frac {\ sqrt {2}} {2}和\ frac {\ sqrt {2}} {2} \\
\ frac {\ sqrt {2}} {2}&-\ frac {\ sqrt {2 }} {2}
\ end {bmatrix}
\ begin {bmatrix}
z_1 \\
z_2
\ end {bmatrix}
$$

搜索“旋转矩阵”。

Olli Cordic旋转也可以表示为线性变换。例如:

$$
I_1 [n + 1] \\
Q_1 [n + 1] \\
I_2 [n + 1] \\
Q_2 [n + 1] \\
\ end {bmatrix}
=
\ begin {bmatrix}
1和2 ^ {-k}&0&0 \\
-2 ^ {-k}&1&0&0 \\
0&0&1&2 ^ {-k} \\
0&0&-2 ^ {-k}&1 \\
\ end {bmatrix}
\ begin {bmatrix}
I_1 [n] \\
Q_1 [n] \\
I_2 [n] \\
Q_2 [n] \\
\ end {bmatrix}
$$

可以吗涂抹中心矩阵以某种方式使数字协同工作以使其收敛更快?

结果确定器可以表示为:

$$
\ begin {aligned}
D&=
\ begin {bmatrix}
I_1 \\
Q_1 \\
I_2 \\
\ end {bmatrix} ^ T
\ begin {bmatrix}
1&0&0 &0 \\
0&1&0&0 \\
0&0&-1&0 \\
0&0&0&-1 \\
\ end {bmatrix}
\ begin {bmatrix}
I_1 \\
Q_1 \\
I_2 \\
Q_2 \\
\ end {bmatrix} \\
&= I_1 ^ 2 + Q_1 ^ 2-I_2 ^ 2-Q_2 ^ 2
\ end {aligned}
$$


如果您稍微模糊一下眼睛,应该会看到类似以下的内容:

$$ x [n + 1] = A \ cdot x [n] $$

and

$$ D = x ^ T \ cdot V \ cdot x $$

当线性变换(A)的输出矢量位于与输入向量方向相同?检查一下:

$$ A \ cdot x = \ lambda x $$

将其插入

$$ x [n + 1] ] = \ lambda x [n] $$

有一点递归:

$$ x [n + p] = \ lambda ^ px [n] $$

Tada,向量问题已通过指数解简化为标量问题。这些向量具有特殊的名称。它们称为特征向量,乘数值($ \ lambda $)被称为特征值。您可能听说过它们。这就是为什么它们很重要的原因。它们构成了解决多维问题的基础空间。

继续。

DanBeasts稍后还会有更多内容。


这些是“ DanBeast_4_9“距离估算值:

 0 0.00  (10000.00,    0.00)  (10000,    0)  10000.00 10000 1.00000 
 1 0.02  ( 9998.48,  174.52)  ( 9998,  175)   9999.53 10003 1.00035 
 2 0.03  ( 9993.91,  348.99)  ( 9994,  349)  10000.09 10004 1.00039 
 3 0.05  ( 9986.30,  523.36)  ( 9986,  523)   9999.69 10002 1.00023 
 4 0.07  ( 9975.64,  697.56)  ( 9976,  698)  10000.39 10002 1.00016 
 5 0.09  ( 9961.95,  871.56)  ( 9962,  872)  10000.09 10004 1.00039 
 6 0.10  ( 9945.22, 1045.28)  ( 9945, 1045)   9999.75 10004 1.00042 
 7 0.12  ( 9925.46, 1218.69)  ( 9925, 1219)   9999.58 10000 1.00004 
 8 0.14  ( 9902.68, 1391.73)  ( 9903, 1392)  10000.35 10001 1.00006 
 9 0.16  ( 9876.88, 1564.34)  ( 9877, 1564)  10000.06 10002 1.00019 
10 0.17  ( 9848.08, 1736.48)  ( 9848, 1736)   9999.84 10000 1.00002 
11 0.19  ( 9816.27, 1908.09)  ( 9816, 1908)   9999.72  9992 0.99923 


对于整数应用程序,我认为仅此而已。

这是代码:

#====================================================================
def DanBeast_4_9( x, y ):

        if (y+y) < x:
           if (y<<2) < x:
              if (y<<3) < x:
                 if (y<<4) < x:
                    L = (x<<9) + (y<<4)
                 else:
                    L = (x<<9) - (x+x) + (y<<5) + (y<<4)
              else:
                 if (y<<4) < (x+x) + x:
                    L = (x<<9) - (x<<2) - (x+x) + (y<<6) + (y<<4) - y
                 else:
                    L = (x<<9) - (x<<3) - (x<<2) + (y<<7) - (y<<4) - (y+y) - y
           else:
              if (y<<3) < (x+x) + x:
                 if (y<<4) < (x<<2) + x:
                    L = (x<<9) - (x<<4) - (x+x) - x + (y<<7) + (y<<3) + (y+y) + y
                 else:
                    L = (x<<9) - (x<<5) + (x<<2) + (y<<7) + (y<<5) + (y<<2) + (y+y)
              else:
                 if (y<<4) < (x<<3) - x:
                    L = (x<<9) - (x<<5) - (x<<2) - (x+x) + (y<<8) - (y<<6) + y
                 else:
                    L = (x<<9) - (x<<5) - (x<<4) + (y<<8) - (y<<5) - (y<<3) + y
        else:
           if (y<<2) < (x+x) + x:
              if (y<<3) < (x<<2) + x:
                 if (y<<4) < (x<<3) + x:
                    L = (x<<9) - (x<<6) + (x<<2) + (y<<8) - (y<<4)
                 else:
                    L = (x<<9) - (x<<6) - (x<<3) + (y<<8) + (y<<2) + y
              else:
                 if (y<<4) < (x<<3) + (x+x) + x:
                    L = (x<<9) - (x<<6) - (x<<4) - (x<<2) + (y<<8) + (y<<5) - (y<<3) + y
                 else:
                    L = (x<<9) - (x<<6) - (x<<5) + (y<<8) + (y<<5) + (y<<3) + (y+y) + y
           else:
              if (y<<3) < (x<<3) - x:
                 if (y<<4) < (x<<4) - (x+x) - x:
                    L = (x<<9) - (x<<7) + (x<<4) + (x<<2) + (y<<8) + (y<<6) - (y<<2) - y
                 else:
                    L = (x<<9) - (x<<7) + (x<<3) - x + (y<<8) + (y<<6) + (y<<3) + (y+y)
              else:
                 if (y<<4) < (x<<4) - x:
                    L = (x<<8) + (x<<7) - (x<<2) + (y<<8) + (y<<6) + (y<<4) + (y<<3)
                 else:
                    L = (x<<8) + (x<<7) - (x<<4) + (y<<8) + (y<<7) - (y<<5) + (y<<2)

        return L # >> 9

#====================================================================


请记住,每个调用仅执行一次L分配。是的,这有点像嵌入在代码中的查找表。

这就是编写它的代码生成器。

import numpy as np
from scipy import stats


#====================================================================
def Main():

        HandleDepth( 2, 6 )
        HandleDepth( 2, 7 )
        HandleDepth( 3, 7 )
        HandleDepth( 3, 8 )
        HandleDepth( 3, 9 )
        HandleDepth( 4, 9 )

        print "#===================================================================="

#====================================================================
def HandleDepth( ArgDepth, ArgHeadroom ):

#---- Build the Tree

        theTree = []

        theLevelIndex = np.zeros( ArgDepth + 1, "i" )

        AddTreeNode( theTree, "RT", 0, ArgDepth, theLevelIndex )    

#---- Print Header

        print "#===================================================================="
        print "def DanBeast_%d_%d( x, y ):" % ( ArgDepth, ArgHeadroom )
        print ""

#---- Generate Code

        for theBranch in theTree:

          theType    = theBranch[0]
          theLevel   = theBranch[1]
          theOrdinal = theBranch[2]

          theHeight = 1 << theLevel

          theRecipHeight = 1.0 / float( theHeight )

          if theType == "IF":
             theX = BuildAsInteger( "x", theOrdinal )
             theY = BuildAsInteger( "y", theHeight )

             theClause = "if " + theY + " < " + theX + ":"
             print ( 4 + 3 * theLevel ) * " ", theClause
          elif theType == "EL":
             print ( 4 + 3 * theLevel ) * " ", "else:"


          if theLevel == ArgDepth:
             theLowSlope  = ( theOrdinal - 1.0 ) * theRecipHeight
             theHighSlope = float( theOrdinal )  * theRecipHeight

             ia, ib = SolveRange( theLowSlope, theHighSlope, ArgHeadroom )          

             theX = BuildAsInteger( "x", ia )
             theY = BuildAsInteger( "y", ib )

             if theY[0:3] == " - ":
                theCombined = theX + theY
             else:
                theCombined = theX + " + " + theY

             print ( 7 + 3 * theLevel ) * " ", "L = " + theCombined

#---- Print Footer

        print ""
        print "        return L # >> %d" % ArgHeadroom
        print ""

        return 

#====================================================================
def AddTreeNode( ArgTree, ArgType, ArgLevel, ArgDepth, ArgLevelIndex ):

#---- Print Results

        ArgLevelIndex[ArgLevel] += 1

#        print ArgLevel * "  ", ArgType, ( 1 << ArgLevel), ArgLevelIndex[ArgLevel]

#---- Add to Tree

        ArgTree.append( [ ArgType, ArgLevel, ArgLevelIndex[ArgLevel] ] )

#---- Check for Terminal Case

        if ArgLevel == ArgDepth:
           return

#---- Add more branches

        AddTreeNode( ArgTree, "IF", ArgLevel + 1, ArgDepth, ArgLevelIndex )
        AddTreeNode( ArgTree, "EL", ArgLevel + 1, ArgDepth, ArgLevelIndex )

#  0 1 1 -1 
#  1 2 1  0   IF0     2 1
#  2 4 1  1      IF1      4 1
#  3 8 1  2         IF2      8 1      0   --> 1/8
#  4 8 2  2         EL2      8 2      1/8 --> 1/4
#  5 4 2  1      EL1      4 2
#  6 8 3  5         IF2      8 3      1/4 --> 3/8
#  7 8 4  5         EL2      8 4      3/8 --> 1/2
#  8 2 2  0   EL0      2 2
#  9 4 3  8      IF1      4 3
# 10 8 5  9         IF2      8 5      1/2 --> 5/8
# 11 8 6  9         EL2      8 6      5/8 --> 3/4
# 12 4 4  8      EL1      4 4
# 13 8 7 12         IF2      8 7      3/4 --> 7/8
# 14 8 8 12         EL2      8 8      7/8 --> 1

#====================================================================
def BuildAsInteger( ArgRef, ArgValue ):

#---- Prepare for Build

        theClause = ""

        b = 16
        v = 1 << b

        r = ArgValue

        c = []

#---- Build Shifty String

        while v > 0:
           ar = abs( r )
           nv = v >> 1

           dt =  v - ar   # Top Distance
           db = ar - nv   # Bottom Distance

           if db >= 0:

              if dt < db:

                 if r > 0:
                    c.append( [b,v] )
                    r -= v
                    theClause += " + " + ShiftyNumberFormat( ArgRef, b )
                 else:
                    theClause += " - " + ShiftyNumberFormat( ArgRef, b )
                    c.append( [b,-v] )
                    r += v

           v  = nv
           b -= 1

#---- Exit

        if theClause[0:3] == " + ":
           return theClause[3:]

        return theClause

#====================================================================
def ShiftyNumberFormat( ArgRef, ArgShift ):

        if ArgShift == 0:
           return ArgRef

        if ArgShift == 1:
           return "(" + ArgRef + "+" + ArgRef + ")"

        return "(" + ArgRef + "<<" + str( ArgShift ) + ")"

#====================================================================
def SolveRange( ArgLowSlope, ArgHighSlope, ArgHeadroom ):

#---- Get the Low End Point

        theLowAngle = np.arctan( ArgLowSlope )
        theLowX     = np.cos( theLowAngle )
        theLowY     = np.sin( theLowAngle )

#---- Get the High End Point

        theHighAngle = np.arctan( ArgHighSlope )
        theHighX     = np.cos( theHighAngle )
        theHighY     = np.sin( theHighAngle )

#---- Generate a Set of Points on the Circumference

        x = np.zeros( 101 )
        y = np.zeros( 101 )

        theSlice = ( theHighAngle - theLowAngle ) * 0.01

        theAngle = theLowAngle

        for s in range( 101 ):
          x[s] = np.cos( theAngle )
          y[s] = np.sin( theAngle )
          theAngle += theSlice

#---- find the Best Fit Line
#  x = v0 y + v1
#  (1/v1) x - (v0/v1) y = 1

        v = stats.linregress( y, x )

        a = 1/v[1]
        b =  -v[0] * a

#---- Get the Integer Coefficients

        p = 1 << ArgHeadroom

        ia = int( p * a + 0.5 )
        ib = int( p * b + 0.5 )

#---- Return Results

        return ia, ib

#====================================================================
Main()


如果您不熟悉代码生成器,请学习此代码生成器,然后戴上“软件工程师”的帽子,然后跳舞。享受吧。

此代码保持不变。

这应该使每个感兴趣的人忙一会儿。我必须将注意力转向另一个项目。


这里是使用最适合浮点数的相同hack线性回归的结果。仍然不太破旧。

 0 0.00  (10000.00,    0.00)  (10000,    0)  10000.00   9996.79 0.99968
 1 0.02  ( 9998.48,  174.52)  ( 9998,  175)   9999.53  10000.25 1.00007
 2 0.03  ( 9993.91,  348.99)  ( 9994,  349)  10000.09  10001.68 1.00016
 3 0.05  ( 9986.30,  523.36)  ( 9986,  523)   9999.69   9999.11 0.99994
 4 0.07  ( 9975.64,  697.56)  ( 9976,  698)  10000.39   9999.25 0.99989
 5 0.09  ( 9961.95,  871.56)  ( 9962,  872)  10000.09  10001.54 1.00014
 6 0.10  ( 9945.22, 1045.28)  ( 9945, 1045)   9999.75  10000.74 1.00010
 7 0.12  ( 9925.46, 1218.69)  ( 9925, 1219)   9999.58   9997.05 0.99975
 8 0.14  ( 9902.68, 1391.73)  ( 9903, 1392)  10000.35  10000.78 1.00004
 9 0.16  ( 9876.88, 1564.34)  ( 9877, 1564)  10000.06  10001.62 1.00016
10 0.17  ( 9848.08, 1736.48)  ( 9848, 1736)   9999.84   9999.49 0.99997


浮点数的额外精度意味着整数情况下的精度限制为允许的净空为9。 11个可能会更好,但也可能需要额外的移位并加一两个。

这是生成的代码。 C语言比Python更具可读性的情况很少见。当然,在C语言中也可以使用相同的整数方法,但是具有浮点版本可能确实有用。很高兴看到实际的数字。

一个典型的语句是C表示距离是:

        d = sqrt( x*x + y*y );


有两个乘数和一个已经用完的款项。现在看一下代码。每个评估仅需两个乘法和一个和。在此之前,有四个“ if”语句,每个语句可能有一些乘数(但乘以2的幂!)。

在我的下一个项目中.....

评论


$ \ begingroup $
两点解决方案是“ alpha max beta min”幅度估计器系列(正如Matt在评论中为我们命名的那样)en.wikipedia.org/wiki/Alpha_max_plus_beta_min_algorithm,有关此方法的更多详细信息,请参见此有趣的帖子如果您的方法更好,那么该人员将其扩展了三个(以及更高的维度):math.stackexchange.com/questions/1282435/…
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月3日,17:56

$ \ begingroup $
@DanBoschen谢谢。以后我必须看看。直到深夜才回来。我不认为他们采用了“禁止乘法”规则。肯定可以将相同的技术扩展到任何N球区域。我认为DanBeastSixteen足以满足您的目的。我不会手动处理系数,所以请耐心等待。额外的深度可以忽略不计的额外处理时间!我还将把它整合到我的“保证正确”的解决方案中,并提高其性能。您如何看待我的新遥控计划?
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
20年1月3日,18:09

$ \ begingroup $
最终将需要乘数或查找表,所以真的不符合要求,对吗?这就是我在开头的评论中关于了解这些方法的意思,但是它们需要加倍运算并且具有有限的误差),因此添加更多的系数可以消除对有限误差的关注,因为只要我能够决定e,我就会对任何误差e都很好-因此,您只需添加更多点即可到达e下方,一切都很好,但随后您需要乘数和查找表来实现。因此,对于我发送的数学站点链接,可能不是一个很好的答案,但可能是一个很好的答案,也许是一个更好的答案。
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月3日,18:13



$ \ begingroup $
如果您有一个通用的解决方案,任何人都可以扩展到任何数量的系数,那么我认为这将是数学站点上该问题的绝妙答案。
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月3日,18:16

$ \ begingroup $
我是否正确解释了这一点?您可以选择2点alpha max beta min的4点选项,但这需要乘数或查找表,或者这确实与此处的目标一致吗?我不认为您可以仅靠平移就能达到任何e并采用这种方法(有限误差)添加,还是我还没有看到它?
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月4日,下午3:02

#8 楼

前言:“#comp共有三种:一种需要精确的算术,而另一种则不需要。”我在这里回收一个古老的双关语:数学家分为三种:可以计数的人和不能计数的人。这是一个非常棘手的问题。这种贡献是适度的,因为它倾向于收集一些选择,而不是一个完整的答案。
我觉得这可能适合这个问题,它混合了:


模拟运算(加,平方根和幂),
模拟近似与离散数格式为$ n $ -ary或($ n = 2 $)二进制格式,
离散的操作成本。

的确,对于抽象算法操作而言(硬件绑定),效率和优化可以显示不同的方面,具体取决于语言,编译,资源,硬件等。输入/输出是否签名/整数/复杂/浮点很重要。
微积分技巧可以限制经典的计算负担。实际上,大多数工程解决方案都是非直接可解决问题的近似值。


模拟操作。

(甚至发明了)使用对数和对数或滑尺或日志表来节省计算产品的时间。傅立叶变换将繁琐的卷积转换为更简单的乘积。如果在基本操作上有一个层次结构,通常认为加法比产品更简单。因此$ a ^ 2-b ^ 2 $(需要两个乘法和一个加法)可能比$(a + b)(ab)$(需要两个加法和一个乘法)效率低。

同样,将两个复数$ a_1 + i a_2 $和$ b_1 + i b_2 $相乘,得到复数乘积:

$$(a_1 + i a_2)(b_1 + i b_2) =(a_1 b_1 -a_2 b_2)+ i(a_1 b_2 + a_2 b_1)$$

需要四个乘法和两个加法。但是使用$ k_1 = a_1(b_1 + b_2)$,$ k_2 = b_2(a_1 + a_2)$和$ k_3 = b_1(a_2 – a_1)$,您将获得$ \ mathrm {Re}(a_1 + i a_2)(b_1 + i b_2)= k_1-k_2 $和$ \ mathrm {Im}(a_1 + i a_2)(b_1 + i b_2)= k_1 + k_3 $。因此,要乘以2,再乘以4。 br />

评论


$ \ begingroup $
请参阅我对计分的最新建议-如果您有任何想法,请等待几天的辩论时间。
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月2日,下午3:34

#9 楼

该答案(第4个!)是第一个答案的摘要重复,其中删除了不必要的代码和说明。这是修订版本,因此该马在比赛中的名称为“ Cedron Revised”。和我将要使用的那个。通过测试,它可能不是绝对最快的,但是它与最快的设备处于同一邻域,并且占用空间小得多,并且没有内部函数调用。

可以通过比较几何均值来简化确定。

$$
\开始{aligned}
D&=(x_1 ^ 2 + y_1 ^ 2)-(x_2 ^ 2 + y_2 ^ 2)\\
& =(x_1 ^ 2-x_2 ^ 2)+(y_1 ^ 2-y_2 ^ 2)\\
&=(x_1-x_2)(x_1 + x_2)+(y_1-y_2)(y_1 + y_2)\ \
&=(x_1-x_2)(x_1 + x_2)-(y_2-y_1)(y_1 + y_2)\\
&= D_x S_x-D_y S_y \\
&= \左(\ sqrt {D_x S_x}-\ sqrt {D_y S_y} \ right)\左(\ sqrt {D_x S_x} + \ sqrt {D_y S_y} \ right)\\
\ end {aligned}
$$

$ D_x,S_x,D_y,S_y \ ge 0 $。

第二个因素始终为正。因此,几何均数差异的符号也将是确定者的符号,并且在不为零时给出正确的答案。

所采用的巧妙技巧可以表述为“如果两个正数在一个正整数之内,互为两个因数,它们的几何平均值将以算术平均值为上限,而以算术平均值的16/17为下限。“

上限很容易证明:

$$
\开始{对齐}
\ sqrt {AB}&\ le \ frac {A + B} {2} \\
2 \ sqrt {AB}& \ le A + B \\
4 AB&\ le A ^ 2 + 2AB + B ^ 2 \\
0&\ le A ^ 2-2AB + B ^ 2 \\
0&\ le(A-B)^ 2 \\
\ end {aligned}
对于任何A和B都是如此。

下界,几乎一样容易:
$$
B \ ge A&\ ge \ frac {B} {2} \\
AB&\ ge \ frac {B ^ 2} {2} \\
\ sqrt {AB}&\ ge \ frac {B} {\ sqrt {2}} \\
&= \ frac {\ frac {B} {\ sqrt {2}}} {(A + B)/ 2} \ cdot \ frac {A + B} {2} \\
&= \ frac { \ sqrt {2}} {1 + A / B} \ cdot \ frac {A + B} {2} \\
&\ ge \ frac {\ sqrt {2}} {1 + 1/2} \ cdot \ frac {A + B} {2} \\
&= \ frac {2} {3} \ sqrt {2} \ cdot \ frac {A + B} {2} \\
&\约0.9428 \ cdot \ frac {A + B} {2} \\
&> \ frac {16} {17} \ cdot \ frac {A + B} {2} \\
&\ approx 0.9412 \ cdot \ frac {A + B} {2} \\
\ end {aligned}
$$

“平方”表示将它们带来的因素彼此成两个因数这是通过反复地将较小的一个乘以两个,直到超过或等于另一个来实现的。两个数字集必须相乘以保持相对。第二个while循环仅对非常少的一组输入值有效。通常,它被视为一个“ if”语句。

过程如下;


将指针移至第一个八分位数
进行简单比较
求和和
求平方的因数
进行代理几何均值比较
进行乘法比较

这是Python中的代码。由于其简单性,可以使用任何语言进行轻松编码。

#====================================================================
def CedronRevised( I1, Q1, I2, Q2 ):

#---- Ensure the Points are in the First Quadrant WLOG

        x1 = abs( I1 )
        y1 = abs( Q1 )

        x2 = abs( I2 )
        y2 = abs( Q2 )

#---- Ensure they are in the Lower Half (First Octant) WLOG

        if y1 > x1:
           x1, y1 = y1, x1

        if y2 > x2:
           x2, y2 = y2, x2

#---- Primary Determination with X Absolute Difference

        if x1 > x2:

           if x1 + y1 >= x2 + y2:
              return 2, 0

           thePresumedResult = 2
           dx = x1 - x2

        elif x1 < x2:

           if x1 + y1 <= x2 + y2:
              return -2, 0

           thePresumedResult = -2
           dx = x2 - x1

        else:

           if y1 > y2:
              return 2, 1
           elif y1 < y2:
              return -2, 1
           else:
              return 0, 1

#---- Sums and Y Absolute Difference

        sx = x1 + x2
        sy = y1 + y2

        dy = abs( y1 - y2 )

#---- Bring Factors into 1/2 to 1 Ratio Range

        while dx <  sx:
              dx += dx

              if dy <= sy:
                 dy += dy
              else:                
                 sy += sy

        while dy <  sy:
              dy += dy

              if dx <= sx:
                 dx += dx
              else:                
                 sx += sx

#---- Use Twice of Arithmetic Mean as Proxy for Geometric Mean

        cx = sx + dx   # >= 2 sqrt(sx*dx) > 16/17 cx
        cy = sy + dy

        cx16 = cx << 4
        cy16 = cy << 4

        if cx16 > cy16 + cy:
           return thePresumedResult, 2

        if cy16 > cx16 + cx:
           return -thePresumedResult, 2

#---- X Multiplication

        px = 0

        while sx > 0:
           if sx & 1:
              px += dx

           dx += dx
           sx >>= 1

#---- Y Multiplication

        py = 0

        while sy > 0:
           if sy & 1:
              py += dy

           dy += dy
           sy >>= 1

#---- Return Results

        if px > py:
           return thePresumedResult, 3

        if px < py:
           return -thePresumedResult, 3

        return 0, 3

#====================================================================



这是我的输入内容,“不一定是100%正确的”类别。如果要求更严格,则可以使用更深,更精确的DanBeast。

#====================================================================
def DanBeast_3_9( I1, Q1, I2, Q2 ):

#---- Ensure the Points are in the First Quadrant WLOG

        x1 = abs( I1 )
        y1 = abs( Q1 )

        x2 = abs( I2 )
        y2 = abs( Q2 )

#---- Ensure they are in the Lower Half (First Octant) WLOG

        if y1 > x1:
           x1, y1 = y1, x1

        if y2 > x2:
           x2, y2 = y2, x2

#---- Primary Determination with Quick Exit

        if x1 > x2:
           if x1 + y1 >= x2 + y2:
              return 2, 0
        elif x1 < x2:
           if x1 + y1 <= x2 + y2:
              return -2, 0
        else:
           if y1 > y2:
              return 2, 0
           elif y1 < y2:
              return -2, 0
           else:
              return 0, 0

#---- Estimate First Multiplied Magnitude

        if (y1+y1) < x1:
           if (y1<<2) < x1:
              if (y1<<3) < x1:
                 L1 = (x1<<9) - x1 + (y1<<5)
              else:
                 L1 = (x1<<9) - (x1<<3) + (y1<<6) + (y1<<5) - (y1+y1)
           else:
              if (y1<<3) < (x1+x1) + x1:
                 L1 = (x1<<9) - (x1<<4) - (x1<<3) + x1 + (y1<<7) + (y1<<4) + (y1<<3)
              else:
                 L1 = (x1<<9) - (x1<<5) - (x1<<3) - (x1+x1) + (y1<<8) - (y1<<6) + (y1<<4) - (y1+y1) - y1
        else:
           if (y1<<2) < (x1+x1) + x1:
              if (y1<<3) < (x1<<2) + x1:
                 L1 = (x1<<9) - (x1<<6) - x1 + (y1<<8) - (y1<<2) - y1
              else:
                 L1 = (x1<<9) - (x1<<6) - (x1<<5) + (x1<<2) + (x1+x1) + (y1<<8) + (y1<<5) + (y1+y1)
           else:
              if (y1<<3) < (x1<<3) - x1:
                 L1 = (x1<<9) - (x1<<7) + (x1<<4) - (x1+x1) + (y1<<8) + (y1<<6) + (y1+y1)
              else:
                 L1 = (x1<<8) + (x1<<7) - (x1<<3) - (x1+x1) + (y1<<8) + (y1<<6) + (y1<<5) - (y1+y1)

#---- Estimate Second Multiplied Magnitude

        if (y2+y2) < x2:
           if (y2<<2) < x2:
              if (y2<<3) < x2:
                 L2 = (x2<<9) - x2 + (y2<<5)
              else:
                 L2 = (x2<<9) - (x2<<3) + (y2<<6) + (y2<<5) - (y2+y2)
           else:
              if (y2<<3) < (x2+x2) + x2:
                 L2 = (x2<<9) - (x2<<4) - (x2<<3) + x2 + (y2<<7) + (y2<<4) + (y2<<3)
              else:
                 L2 = (x2<<9) - (x2<<5) - (x2<<3) - (x2+x2) + (y2<<8) - (y2<<6) + (y2<<4) - (y2+y2) - y2
        else:
           if (y2<<2) < (x2+x2) + x2:
              if (y2<<3) < (x2<<2) + x2:
                 L2 = (x2<<9) - (x2<<6) - x2 + (y2<<8) - (y2<<2) - y2
              else:
                 L2 = (x2<<9) - (x2<<6) - (x2<<5) + (x2<<2) + (x2+x2) + (y2<<8) + (y2<<5) + (y2+y2)
           else:
              if (y2<<3) < (x2<<3) - x2:
                 L2 = (x2<<9) - (x2<<7) + (x2<<4) - (x2+x2) + (y2<<8) + (y2<<6) + (y2+y2)
              else:
                 L2 = (x2<<8) + (x2<<7) - (x2<<3) - (x2+x2) + (y2<<8) + (y2<<6) + (y2<<5) - (y2+y2)

#---- Return Results

        if L1 < L2:
           return -1, 2

        return 1, 2

#====================================================================


这是野兽,但运行速度很快。与原始DanBeast4相比,错误的数量大约是错误的1/3。两者都比Olli的Cordic方法要好。

不要太紧抓这些时机。计分是准确的。

Algorithm         Correct    Time      Score    Penalties  Eggs
---------------   -------    ------    -------  ---------  ----
  Empty Economy    49.86     2.6425     472849    2378650    0
   Empty Deluxe     0.05     2.7039       1944  474168000  243
Starter Economy    89.75     2.8109     851367     486060    0
 Starter Deluxe    90.68     2.8986    1663118     441920  151

    Walk On One    93.58     2.8282     887619     304800    0
    Walk On Two    93.58     2.7931     887619     304800    0

 Dan Beast Four    99.85     2.9718    1750076       7130  151
  Dan Beast 3_9    99.95     2.9996    1750996       2530  151
Cedron Unrolled   100.00     3.0909    1898616          0  243
 Cedron Revised   100.00     3.1709    1898616          0  243
  Cedron Deluxe   100.00     3.1734    1898616          0  243
   Olli Revised    99.50     3.1822    1728065      23880    0
  Olli Original    99.50     3.2420    1728065      23880    0

Cedron Multiply   100.00     3.2148    1898616          0  243
  Matt Multiply   100.00     3.3242    1898616          0  243


我们进行了几次试穿:

#====================================================================
def WalkOnOne( I1, Q1, I2, Q2 ):

        x1 = abs( I1 )
        y1 = abs( Q1 )

        x2 = abs( I2 )
        y2 = abs( Q2 )

        s1 = x1 + y1
        s2 = x2 + y2

        D = s1 - s2

        if D < 0:
           return -1, 0

        return 1, 0

#====================================================================
def WalkOnTwo( I1, Q1, I2, Q2 ):

        s1 = abs( I1 ) + abs( Q1 )
        s2 = abs( I2 ) + abs( Q2 )

        if s1 < s2:
           return -1, 0

        return 1, 0

#====================================================================



>这小部分与DanBeast解决方案有关,但由于该答案已达到要求的能力,因此我在此处添加它。

从C到0到45度的角度以0.1为增量的浮点DanBeasts的结果。使用float值就像H参数是60。换句话说,这些图表中的任何错误都是由于直线与曲线的最佳拟合,而不是直线的最佳整数系数。

D                    Depth, first specification parameter

Min,Max,Ave,Std Dev  Estimate/Actual results

MinB, MaxB           Log_2(1-Min), Log_2(Max-1)

H                    Headroom, second specification parameter

D     Min         Max        Ave        Std Dev   MinB  MaxB    H
- ----------  ----------  ----------  ---------- ----- -----   --
0 0.94683054  1.02671481  1.00040437  0.02346769  -4.2  -5.2    5
1 0.98225695  1.00919519  1.00011525  0.00668514  -5.8  -6.8    6
2 0.99505899  1.00255518  1.00002925  0.00170539  -7.7  -8.6    8
3 0.99872488  1.00065730  1.00000719  0.00042868  -9.6 -10.6   10
4 0.99967861  1.00016558  1.00000181  0.00010727 -11.6 -12.6   12
5 0.99991949  1.00004147  1.00000044  0.00002685 -13.6 -14.6   14


每个在D中增加表示if-tree大小加倍。 Ave值的偏斜反映了未使用最佳最佳拟合度量。这些数字用于x与y的函数的线性回归拟合。 H列为每个D级提供建议的净空参数。每级增加约2位。使用小于此值的值意味着整数系数误差将占线误差的最佳拟合。

这是又一次竞赛,增加了新的匹数。

br />
时间值粗糙且嘈杂,不应被认为是决定性的。

“ Walk On Cheat”是使用Python乘法的两个乘法解决方案。毫不奇怪,它的运行速度明显更快。

“三步行走”和“四步行走”分别是max_min和近似于Old Engineer的方程式。相关摘要:

Algorithm         Correct    Time      Score    Penalties  Eggs
---------------   -------    ------    -------  ---------  ----
  Empty Economy    49.86     3.0841     472849    2378650    0
   Empty Deluxe     0.05     3.0433       1944  474168000  243
Starter Economy    89.75     3.1843     851367     486060    0
 Starter Deluxe    93.88     3.1376    1693416     290430  151

  Walk On Cheat   100.00     2.9710    1898616          0  243
    Walk On One    93.58     3.1043     887619     304800    0
    Walk On Two    93.58     3.0829     887619     304800    0
  Walk On Three    97.90     3.2090     928619      99800    0
   Walk On Four    97.96     3.4982     929267      96560    0

   Olli Revised    99.50     3.3451    1728065      23880    0
  Olli Original    99.50     3.4025    1728065      23880    0

 Dan Beast Four    99.85     3.2680    1750076       7130  151
  Dan Beast 3_9    99.95     3.3335    1750996       2530  151
 Dan Beast 3_10    99.97     3.3476    1751206       1480  151
 Dan Beast 3_11    99.97     3.2893    1751220       1410  151

Cedron Unrolled   100.00     3.2740    1898616          0  243
 Cedron Revised   100.00     3.2747    1898616          0  243
  Cedron Deluxe   100.00     3.3309    1898616          0  243

Cedron Multiply   100.00     3.3494    1898616          0  243
  Matt Multiply   100.00     3.4944    1898616          0  243


对“入门豪华版”进行了一些微调,以在初步确定后返回与假定结果相反的结果。出于比较目的,增加了DanBeast人口。但是,Olli会让我感到惊讶。

现在测试代码太大了,无法发布。任何需要复制或DanBeast逻辑的两个代码生成器(Python和C)的人都可以通过cedron ta exede tod net向我发送电子邮件。我相信它将为编程课程提供出色的指导材料。

评论


$ \ begingroup $
太好了!如果这是最佳答案,您是否可以删除其他三个答案(或在该答案底部的重要内容中添加任何重要内容)?这真的可以清除这篇文章中的内容
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月7日在16:36

$ \ begingroup $
谢谢,但是抱歉,我不能。它们将超出空间限制。第一个仍然是有效的,因为它是源。第二部分演示了“主要确定”逻辑的属性。第三个是DanBeast解决方案,它在本质上有很大不同,但仍然可行。这是我提交的供您考虑的一个。我认为删除其中任何一个将是一种损失。如果这个项目获胜(我认为它将以几乎任何标准获胜),它将在未来排名第一。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
20年1月7日在16:42

$ \ begingroup $
知道了,我们很乐意尝试一下,我们很乐意运行这些程序!
$ \ endgroup $
–丹·博申(Dan Boschen)
20年1月7日在16:49

$ \ begingroup $
@DanBoschen我提供了我推荐的DanBeast版本。在时间和准确性上都超过了Olli。后续操作是有关公民的简单度量| a |的实现。 + | b |。
$ \ endgroup $
–雪铁龙·道格(Cedron Dawg)
20年1月7日在17:21

$ \ begingroup $
所以,我想知道,如果我喜欢您的方法,是否应该对此表示赞同?并且-抱歉,我有点不知所措-它实际上如何比较(执行时间)与“愚蠢的方式”进行比较,计算幅度然后进行比较?我试图在微控制器上的FFT中找到两个峰值,但实际上并不需要所有其他点的实数值。
$ \ endgroup $
–阿森纳
20 Mar 18 '20在15:18