线性反馈移位寄存器?

时间:2010-09-17 12:16:11

标签: python language-agnostic digital-logic

最近我反复讨论LFSR的概念,我发现它非常有趣,因为它与不同的领域有联系并且本身也很吸引人。我花了一些力气去理解,最后的帮助是真的很好page,比(起初)神秘的wikipedia entry要好得多。所以我想为一个像LFSR一样工作的程序编写一些小代码。更确切地说,它以某种方式展示了LFSR的工作原理。这是在经过一些长篇尝试(Python)之后我能想到的最干净的事情:

def lfsr(seed, taps):
    sr, xor = seed, 0
    while 1:
        for t in taps:
            xor += int(sr[t-1])
        if xor%2 == 0.0:
            xor = 0
        else:
            xor = 1
        print xor
        sr, xor = str(xor) + sr[:-1], 0
        print sr
        if sr == seed:
            break

lfsr('11001001', (8,7,6,1))      #example

我将XOR函数的输出命名为“xor”,不是很正确。 但是,这只是为了说明它如何圈出其可能的状态,实际上您注意到寄存器由字符串表示。没有多少逻辑连贯性。

这可以很容易地变成一个你可以看几个小时的好玩具(至少我可以: - )

def lfsr(seed, taps):
    import time
    sr, xor = seed, 0
    while 1:
        for t in taps:
            xor += int(sr[t-1])
        if xor%2 == 0.0:
            xor = 0
        else:
            xor = 1
        print xor
        print
        time.sleep(0.75)
        sr, xor = str(xor) + sr[:-1], 0
        print sr
        print
        time.sleep(0.75)

然后它让我印象深刻,这在编写软件时有什么用?我听说它可以生成随机数;这是真的吗?怎么样? 所以,如果有人能够这样做会很好:

  • 解释如何在软件开发中使用此类设备
  • 提出一些代码,以支持上述观点,或者像我一样,以任何语言显示不同的方法

另外,由于关于这段逻辑和数字电路并没有太多的教学内容,如果这可以成为noobies(像我一样)的地方,以便更好地理解这个的东西,或者更好,了解它是什么以及在编写软件时它是如何有用的。应该把它变成一个社区维基吗?

那就是说,如果有人觉得喜欢打高尔夫......那么欢迎你。

9 个答案:

答案 0 :(得分:11)

由于我在Python中寻找LFSR实现,我偶然发现了这个主题。然而,我发现根据我的需要,以下内容更准确:

def lfsr(seed, mask):
    result = seed
    nbits = mask.bit_length()-1
    while True:
        result = (result << 1)
        xor = result >> nbits
        if xor != 0:
            result ^= mask

        yield xor, result

上述LFSR发生器基于GF(2 k )模量计算(GF = Galois Field)。刚完成代数课程后,我将以数学方式解释这一点。

让我们从例如GF(2 4 )开始,等于{a 4 x 4 + a 3 x 3 + a 2 x 2 + a 1 x < sup> 1 + a 0 x 0 | a 0 1 ,..., 4 ∈Z 2 }(澄清,Z < sub> n = {0,1,...,n-1}因此Z 2 = {0,1},即一位)。这意味着这是所有第四度多项式的集合,其中所有因子都存在或不存在,但没有这些因子的倍数(例如,没有2x k )。 x 3 ,x 4 + x 3 ,1和x 4 + x 3 + x 2 + x + 1都是该组成员的例子。

我们将该集合模数作为第四度的多项式(即,P(x)∈GF(2 4 )),例如, P(x)= x 4 + x 1 + x 0 。对组的这种模量操作也表示为GF(2 4 )/ P(x)。供您参考,P(x)描述了&#39;&#39;在LFSR内。

我们还采用3阶或更低阶的随机多项式(因此它不受我们的模数影响,否则我们也可以直接在其上执行模运算),例如, A 0 (x)= x 0 。现在每个后续的A i (x)通过乘以x来计算:A i (x)= A i-1 (x) * x mod P(x)。

由于我们处于有限的范围内,模数运算可能会产生影响,但只有当得到的A i (x)至少有一个因子x 4 时(我们在P(x)中的最高因子)。请注意,由于我们正在处理Z 2 中的数字,因此执行模数运算本身只不过是通过添加两个来确定每个 i 是否变为0或1来自P(x)和A i (x)的值一起(即,0 + 0 = 0,0 + 1 = 1,1 + 1 = 0,或者&#39; xoring&#39;这两个)。

每个多项式可以写成一组位,例如x 4 + x 1 + x 0 ~10011。 0 (x)可以看作是种子。 &#39;次x&#39;操作可以看作是左移操作。模数运算可以看作是一个掩码操作,掩码是我们的P(x)。

上面描述的算法因此生成(无限流)有效的四位LFSR模式。例如,对于我们定义的A 0 (x)(x 0 和P(x)(x 4 + x 1 + x 0 ,我们可以在GF中定义以下第一个产生的结果(2 4 )(注意A 0 直到第一轮结束时才会产生 - 数学家通常开始计算在&#39; 1&#39;):

 i   Ai(x)                   'x⁴'  bit pattern
 0   0x³ + 0x² + 0x¹ + 1x⁰   0     0001        (not yielded)
 1   0x³ + 0x² + 1x¹ + 0x⁰   0     0010
 2   0x³ + 1x² + 0x¹ + 0x⁰   0     0100
 3   1x³ + 0x² + 0x¹ + 0x⁰   0     1000
 4   0x³ + 0x² + 1x¹ + 1x⁰   1     0011        (first time we 'overflow')
 5   0x³ + 1x² + 1x¹ + 0x⁰   0     0110
 6   1x³ + 1x² + 0x¹ + 0x⁰   0     1100
 7   1x³ + 0x² + 1x¹ + 1x⁰   1     1011
 8   0x³ + 1x² + 0x¹ + 1x⁰   1     0101
 9   1x³ + 0x² + 1x¹ + 0x⁰   0     1010
10   0x³ + 1x² + 1x¹ + 1x⁰   1     0111
11   1x³ + 1x² + 1x¹ + 0x⁰   0     1110
12   1x³ + 1x² + 1x¹ + 1x⁰   1     1111
13   1x³ + 1x² + 0x¹ + 1x⁰   1     1101
14   1x³ + 0x² + 0x¹ + 1x⁰   1     1001
15   0x³ + 0x² + 0x¹ + 1x⁰   1     0001        (same as i=0)

请注意,您的面具必须包含&#39; 1&#39;在第四个位置,以确保您的LFSR生成四位结果。另请注意,&#39; 1&#39;必须出现在第零位置,以确保您的比特流不会以0000位模式结束,或者最后一位将变为未使用(如果所有位都向左移位,您最终也会得到零一班后的第0个位置。)

并非所有P(x)都必然是GF的发生器(2 k )(即,并非所有k位掩码都产生所有2 k-1 -1数字)。例如,x 4 + x 3 + x 2 + x 1 + x 0 产生3组,每组5个不同的polynomals,或&#34; 3个周期的5&#34;:0001,0010,0100,1000,1111; 0011,0110,1100,0111,1110;和0101,1010,1011,1001,1101。请注意,0000永远不会生成,也不能生成任何其他数字。

通常,LFSR的输出是“移位”的位。 out,这是一个&#39; 1&#39;如果执行模数运算,则&#39; 0&#39;什么时候不是。 LFSR的周期为2 k-1 -1,也称为伪噪声或PN-LFSR,遵循Golomb的随机性假设,其表示为就像这个输出位是随机的足够的&#39;

因此,这些位的序列可用于密码学,例如A5 / 1和A5 / 2移动加密标准或E0蓝牙标准。但是,它们并不像人们想象的那样安全:Berlekamp-Massey algorithm可用于反向设计LFSR的特征多项式(P(x))。因此,强加密标准使用Non-linear FSR或类似的非线性函数。与此相关的主题是AES中使用的S-Boxes


请注意,我使用了int.bit_length() operation。直到Python 2.7才实现 如果你只喜欢有限位模式,你可以检查种子是否等于结果,然后打破你的循环。
你可以在for循环中使用我的LFSR方法(例如for xor, pattern in lfsr(0b001,0b10011)),或者你可以在方法的结果上重复调用.next()操作,每次返回一个新的(xor, result) - 对

答案 1 :(得分:5)

实际上,基于LFSR的算法非常常见。 CRC实际上直接基于LFSR。当然,在计算机科学课上,当人们谈论输入值应如何与累积值进行异或时,人们会谈论多项式,而在电子工程中我们谈论的是水龙头。它们只是不同的术语。

CRC32是一个非常常见的。它用于检测以太网帧中的错误。这意味着当我发布这个答案时,我的PC使用基于LFSR的算法来生成IP数据包的散列,以便我的路由器可以验证其传输的内容是否已损坏。

Zip和Gzip文件是另一个例子。两者都使用CRC进行错误检测。 Zip使用CRC32,Gzip使用CRC16和CRC32。

CRC基本上是散列函数。它足以让互联网发挥作用。这意味着LFSR是相当好的散列函数。我不确定你是否知道这一点,但一般来说好的哈希函数被认为是好的随机数生成器。但是LFSR的问题是选择正确的抽头(多项式)对于散列/随机数的质量非常重要。

您的代码通常是玩具代码,因为它运行在一串零和一串零。在现实世界中,LFSR处理一个字节中的位。您通过LFSR的每个字节都会更改寄存器的累计值。该值实际上是您通过寄存器推送的所有字节的校验和。将该值用作随机数的两种常用方法是使用计数器并通过寄存器推送一系列数字,从而将线性序列1,2,3,4转换为某些散列序列,如15306,22,5587, 994,或者将当前值反馈到寄存器中,以看似随机的顺序生成一个新数字。

应该注意的是,使用bit-fiddling LFSR天真地执行此操作非常慢,因为您必须一次处理位。因此人们已经想出了使用预先计算的表来一次做8位甚至32位的方法。这就是为什么你几乎从未在野外看到LFSR代码。在大多数生产代码中,它伪装成其他东西。

但有时候,一个简单的笨拙LFSR可以派上用场。我曾经为PIC micro编写了一个Modbus驱动程序,该协议使用了CRC16。预先计算的表需要256个字节的内存,而我的CPU只有68个字节(I'm not kidding)。所以我不得不使用LFSR。

答案 2 :(得分:2)

LFSR有很多应用。其中之一就是产生噪音,例如SN76489和变体(用于Master System,Game Gear,MegaDrive,NeoGeo Pocket ......)使用LFSR产生白噪声/周期噪声。对SN76489的LFSR in this page进行了非常好的描述。

答案 3 :(得分:1)

为了使它非常优雅和Pythonic,尝试创建一个生成器,yield来自LFSR的连续值。此外,与浮点0.0进行比较是不必要的,也是令人困惑的。

LFSR只是在计算机中创建伪随机数的众多方法之一。伪随机,因为数字不是真正随机 - 您可以通过从种子(初始值)开始并继续进行相同的数学运算来轻松地重复它们。

答案 4 :(得分:1)

下面是使用整数和二元运算符而不是字符串的代码变体。它也像有人建议的那样使用收益率。

def lfsr2(seed, taps):
    sr = seed
    nbits = 8
    while 1:
        xor = 1
        for t in taps:
            if (sr & (1<<(t-1))) != 0:
                xor ^= 1
        sr = (xor << nbits-1) + (sr >> 1)
        yield xor, sr
        if sr == seed:
            break

nbits = 8
for xor, sr in lfsr2(0b11001001, (8,7,6,1)):
    print xor, bin(2**nbits+sr)[3:]

答案 5 :(得分:1)

这是我的 Python 库之一 - pylfsr 来实现 LFSR。我试图使它成为一种高效的,可以处理任何长度的 LFSR 以生成二进制序列。

import numpy as np
from pylfsr import LFSR

#for 5-bit LFSR with polynomial  x^5 + x^4 +  x^3 + x^2 +1
seed = [0,0,0,1,0]
fpoly = [5,4,3,2]
L = LFSR(fpoly=fpoly,initstate =seed)
seq = L.runKCycle(10)

您也可以在步骤中显示所有信息,

state = [1,1,1]
fpoly = [3,2]
L = LFSR(initstate=state,fpoly=fpoly,counter_start_zero=False)
print('count \t state \t\toutbit \t seq')
print('-'*50)
for _ in range(15):
    print(L.count,L.state,'',L.outbit,L.seq,sep='\t')
    L.next()
print('-'*50)
print('Output: ',L.seq)

输出

count    state      outbit   seq
--------------------------------------------------
1   [1 1 1]     1   [1]
2   [0 1 1]     1   [1 1]
3   [0 0 1]     1   [1 1 1]
4   [1 0 0]     0   [1 1 1 0]
5   [0 1 0]     0   [1 1 1 0 0]
6   [1 0 1]     1   [1 1 1 0 0 1]
7   [1 1 0]     0   [1 1 1 0 0 1 0]
8   [1 1 1]     1   [1 1 1 0 0 1 0 1]
9   [0 1 1]     1   [1 1 1 0 0 1 0 1 1]
10  [0 0 1]     1   [1 1 1 0 0 1 0 1 1 1]
11  [1 0 0]     0   [1 1 1 0 0 1 0 1 1 1 0]
12  [0 1 0]     0   [1 1 1 0 0 1 0 1 1 1 0 0]
13  [1 0 1]     1   [1 1 1 0 0 1 0 1 1 1 0 0 1]
14  [1 1 0]     0   [1 1 1 0 0 1 0 1 1 1 0 0 1 0]
--------------------------------------------------
Output:  [1 1 1 0 0 1 0 1 1 1 0 0 1 0 1]

也可以这样形象化

enter image description here

查看Documentation here

答案 6 :(得分:0)

如果我们假设种子是一个int而不是一个字符串的列表(或者如果不是则将其转换),那么以下内容应该做得更加优雅:

def lfsr(seed, taps) :
  while True:
    nxt = sum([ seed[x] for x in taps]) % 2
    yield nxt
    seed = ([nxt] + seed)[:max(taps)+1]

示例:

for x in lfsr([1,0,1,1,1,0,1,0,0],[1,5,6]) :
  print x

答案 7 :(得分:0)

list_init=[1,0,1,1]
list_coeff=[1,1,0,0]
out=[]
for i in range(15):
    list_init.append(sum([list_init[i]*list_coeff[i] for i in range(len(list_init))])%2)
    out.append(list_init.pop(0))
print(out)

#https://www.rocq.inria.fr/secret/Anne.Canteaut/encyclopedia.pdf

答案 8 :(得分:0)

这里有一段代码,您可以在其中选择您的种子,位数和所需的分接头:

from functools import reduce 

def lfsr(seed=1, bits=8, taps=[8, 6, 5, 4]):
    """
            1 2 3 4 5 6 7 8 (bits == 8)
           ┌─┬─┬─┬─┬─┬─┬─┬─┐
        ┌─→│0│1│0│1│0│0│1│1├─→
        │  └─┴─┴─┴┬┴┬┴─┴┬┴─┘
        └──────XOR┘ │   │
                └──XOR──┘ (taps == 7, 5, 4)
    """
    taps = [bits - tap for tap in taps]
    r = seed & (1 << bits) - 1
    while(1):        
        tap_bits = [(r >> tap) & 1 for tap in taps]        
        bit = reduce(lambda x, y : x ^ y, tap_bits)
        yield r & 1
        r &= (1 << bits) - 1
        r = (r >> 1) | (bit << (bits - 1))