基于Excel的QR二维码生成工具——原理及算法详解(之三)

上一篇文章中我们讨论了使用工作表函数计算RS码的算法,接下来,我们将讨论生成多项式的计算以及RS码计算的VBA算法。在已经有了工作表函数计算RS码的情况下,还要探讨使用VBA的原因是因为VBA的计算速度比工作表函数快得多。

在开始探讨生成多项式之前,必须要说明的是,在QR码标准GB18284-2000的附录中已经附带了所有在QR计算中可能会需要的生成多项式,甚至不需要的都一并附上了:
这里写图片描述
但是,有意思的是,RS码的生成多项式其实并不止一个,而且QR标准中给出的并不是标准生成多项式。我尝试了使用标准生成多项式所计算的纠错码,将其填充到QR矩阵中后,同样能够正确解码而且能够正确地纠错。因此,如果没有耐心,只想寻求干货的同学可以绕过这一段,而有兴趣的同学可以一起探讨一下,使用不同的生成多项式来创建与QR标准中完全不同的二维码。

我们知道标准生成多项式的公式为:

g(x)=(x+a)(x+a 2 )(x+a 3 )(x+a nk ) 
<script id="MathJax-Element-24310" type="math/tex; mode=display">g(x)=(x+a)(x+a^2)(x+a^3)\cdots(x+a^{n-k})</script>,从这个公式可以看出来其实这是一个非常典型的递进算法,如果令 nk=v  <script id="MathJax-Element-24311" type="math/tex">n-k=v</script>,且另上式为 g v (x)  <script id="MathJax-Element-24312" type="math/tex">g_v(x)</script>,那么我们可以看到:
g 1 (x)=(x+a) 
<script id="MathJax-Element-24313" type="math/tex; mode=display">g_1(x)=(x+a)</script>
g 2 (x)=(x+a)(x+a 2 )=g 1 (x)(x+a 2 ) 
<script id="MathJax-Element-24314" type="math/tex; mode=display">g_2(x)=(x+a)(x+a^2)=g_1(x)(x+a^2)</script>以至于一般地
g v (x)=g v1 (x)(x+a v ) 
<script id="MathJax-Element-24315" type="math/tex; mode=display">g_v(x)=g_{v-1}(x)(x+a^v)</script>

我们可以如同上一节中一样,将上述多项式的计算通过一个表格来实现:

v= x  <script id="MathJax-Element-24316" type="math/tex">x</script>的幂次 x 0  <script id="MathJax-Element-24317" type="math/tex">x^0</script> x 1   <script id="MathJax-Element-24318" type="math/tex">x^1</script> x 2   <script id="MathJax-Element-24319" type="math/tex">x^2</script> x 3   <script id="MathJax-Element-24320" type="math/tex">x^3</script> x 4   <script id="MathJax-Element-24321" type="math/tex">x^4</script> x 5   <script id="MathJax-Element-24322" type="math/tex">x^5</script>
g 1 (x)  <script id="MathJax-Element-24323" type="math/tex">g_1(x)</script> a  <script id="MathJax-Element-24324" type="math/tex">a</script>的幂次 1 0
乘数 a <script id="MathJax-Element-24325" type="math/tex">a</script>的幂次20

上表中使用不同的列代表多项式的不同次数的项,并且将系数写到单元格中,可见第二行表示的就是 g 1 (x)  <script id="MathJax-Element-24326" type="math/tex">g_1(x)</script>,因为这个多项式的 x 0   <script id="MathJax-Element-24327" type="math/tex">x^0</script>项系数为 a  <script id="MathJax-Element-24328" type="math/tex">a</script>的1次幂,而x 1  <script id="MathJax-Element-24329" type="math/tex">x^1</script>项系数为 a  <script id="MathJax-Element-24330" type="math/tex">a</script>的0次幂,也就是(x+a) <script id="MathJax-Element-24331" type="math/tex">(x+a)</script>。同理,“乘数”行表示的多项式就是 (x+a 2 )  <script id="MathJax-Element-24332" type="math/tex">(x+a^2)</script>了。那么要计算 g 2 (x)  <script id="MathJax-Element-24333" type="math/tex">g_2(x)</script>需要用乘数乘以 g 1 (x)  <script id="MathJax-Element-24334" type="math/tex">g_1(x)</script>,在表格中就是用乘数的所有系数分别乘以 g 1 (x)  <script id="MathJax-Element-24335" type="math/tex">g_1(x)</script>的所有系数,并且进行恰当的移位:

v= x  <script id="MathJax-Element-24336" type="math/tex">x</script>的幂次 x 0  <script id="MathJax-Element-24337" type="math/tex">x^0</script> x 1   <script id="MathJax-Element-24338" type="math/tex">x^1</script> x 2   <script id="MathJax-Element-24339" type="math/tex">x^2</script> x 3   <script id="MathJax-Element-24340" type="math/tex">x^3</script> x 4   <script id="MathJax-Element-24341" type="math/tex">x^4</script> x 5   <script id="MathJax-Element-24342" type="math/tex">x^5</script>
g 1 (x)  <script id="MathJax-Element-24343" type="math/tex">g_1(x)</script> a  <script id="MathJax-Element-24344" type="math/tex">a</script>的幂次 1 0
乘数 a <script id="MathJax-Element-24345" type="math/tex">a</script>的幂次20
g 1 (x)  <script id="MathJax-Element-24346" type="math/tex">g_1(x)</script>乘以2 a  <script id="MathJax-Element-24347" type="math/tex">a</script>的幂次 3 2
g 1 (x) <script id="MathJax-Element-24348" type="math/tex">g_1(x)</script>乘以0 a  <script id="MathJax-Element-24349" type="math/tex">a</script>的幂次 1 0
g 2 (x) <script id="MathJax-Element-24350" type="math/tex">g_2(x)</script> a  <script id="MathJax-Element-24351" type="math/tex">a</script>的幂次 3 26 0

上表中表示了这样的计算,请注意g 1 (x) <script id="MathJax-Element-24352" type="math/tex">g_1(x)</script>乘以0的结果是向右移位的,这是因为“0”项的次数为1,因此乘以 g 1 (x)  <script id="MathJax-Element-24353" type="math/tex">g_1(x)</script>以后会使它的多项式所有项的幂次升高一次,在表格上就表示为右移一位。然后将两次计算的结果相加,就得到了 g 2 (x)  <script id="MathJax-Element-24354" type="math/tex">g_2(x)</script>的多项式

同理我们可以用上面的表格计算 g 3 (x)  <script id="MathJax-Element-24355" type="math/tex">g_3(x)</script>乃至于 g v (x)  <script id="MathJax-Element-24356" type="math/tex">g_v(x)</script>

v= x  <script id="MathJax-Element-24357" type="math/tex">x</script>的幂次 x 0  <script id="MathJax-Element-24358" type="math/tex">x^0</script> x 1   <script id="MathJax-Element-24359" type="math/tex">x^1</script> x 2   <script id="MathJax-Element-24360" type="math/tex">x^2</script> x 3   <script id="MathJax-Element-24361" type="math/tex">x^3</script> x 4   <script id="MathJax-Element-24362" type="math/tex">x^4</script> x 5   <script id="MathJax-Element-24363" type="math/tex">x^5</script>
g 2 (x)  <script id="MathJax-Element-24364" type="math/tex">g_2(x)</script> a  <script id="MathJax-Element-24365" type="math/tex">a</script>的幂次 3 26 0
乘数 a <script id="MathJax-Element-24366" type="math/tex">a</script>的幂次30
g 2 (x)  <script id="MathJax-Element-24367" type="math/tex">g_2(x)</script>乘以3 a  <script id="MathJax-Element-24368" type="math/tex">a</script>的幂次 6 29 3
g 2 (x) <script id="MathJax-Element-24369" type="math/tex">g_2(x)</script>乘以0 a  <script id="MathJax-Element-24370" type="math/tex">a</script>的幂次 3 26 0
g 3 (x) <script id="MathJax-Element-24371" type="math/tex">g_3(x)</script> a  <script id="MathJax-Element-24372" type="math/tex">a</script>的幂次 6 201 199 0

在Excel中可以很容易根据上面的表格来构造g(x) <script id="MathJax-Element-24373" type="math/tex">g(x)</script>的计算公式矩阵:
$g(x)$计算

需要注意的是,在上面的算法中表格的最左列是 x  <script id="MathJax-Element-24374" type="math/tex">x</script>的最低次幂,而在RS计算表格中,最左列是x <script id="MathJax-Element-24375" type="math/tex">x</script>的最高次幂,因此在使用计算结果的时候,需要进行一次倒序,同时还需要将 a  <script id="MathJax-Element-24376" type="math/tex">a</script>的幂次转化成系数才行

好了,现在我们已经完成了生成多项式的构造,并且探讨了RS码的表格式计算算法。已经完全可以开始进行QR的制作了么?不,没那么简单,使用工作表函数的RS码在进行小规模计算时,计算速度是完全可以胜任的,但是,如果填充的是高版本的QR码,计算量就非常可观了。通常情况下,一个QR码需要对长达上百位的数据码计算30个纠错码,并且因为QR的分段设计,同样的纠错码计算需要重复几十次,这样就导致计算表十分庞大,计算次数达到上百万次。我验证了一下,这样的表格在我的电脑上需要大约25秒才能完成计算,不具备实用性。因此,下面的VBA代码被用来实现同样的RS计算功能,但结果可以瞬间得到:

Private Function ECCALC(dataWords, N, k, gp)
' function that calculates error correction code of the input code words by generate polynomial
' codewords as first argument is the code word for which EC code is calculated
' n as second argument total number of code words
' k as third argument defines the number of data code words, thus n-k is the number of error correction code words
' output of the function is also defined as an array that contains the error correction code
' the error correction code is calculated on the galois field with primitive polynomial= x^8+x^4+x^3+x^2+1=0
'
Dim gpWords(), cWords(), dWords(), prod() As Long
ReDim gpWords(N - k), cWords(N), dWords(N), prod(N - k)
Dim divider As Long
Dim num, EC As Variant
Dim i,j As Long

    With Sheets("RS Calc")
        For i = 0 To 254
            GF(i) = .Cells(7, i + 6)
            GFP(i) = .Cells(8, i + 6)
        Next
    End With
    'read genpoly
    i = 0
    For Each num In dataWords
        If IsNumeric(num) Then
            cWords(i) = num
            i = i + 1
        End If
    Next
    i = 0
    For Each num In gp
        If IsNumeric(num) Then
            gpWords(i) = num
            i = i + 1
        End If
    Next
    'RS calculation starts
    For i = 0 To k - 1
        divider = cWords(0)
        For j = 0 To N - 2     'calculate next code words by multiply genopoly with divider, and substract code word
            If j < N - k And divider <> 0 Then
                num = cWords(j + 1) Xor nGfMult(divider, gpWords(j + 1))
            Else
                num = cWords(j + 1) Xor 0
            End If
            cWords(j) = num
        Next
    Next
    ECCALC = cWords    
End Function
posted @ 2017-09-27 23:22  JackiePENG  阅读(4)  评论(0编辑  收藏  举报  来源