线性反馈移位寄存器?

新手上路,请多包涵

最近我反复接触到 LFSR 的概念,我觉得它很有趣,因为它与不同领域的联系,而且它本身也很吸引人。我花了一些努力才明白,最后的帮助是这个非常好的 页面,比(起初)神秘的 维基百科条目 要好得多。所以我想为一个像 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)

然后我突然想到,这在编写软件中有什么用?我听说它可以生成随机数;是真的吗?如何?所以,如果有人可以:

  • 解释如何在软件开发中使用这样的设备
  • 想出一些代码来支持上面的观点,或者像我一样用任何语言展示不同的方法

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

也就是说,如果有人想打高尔夫球……不客气。

原文由 MattiaG 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 606
2 个回答

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

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

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

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

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

应该注意的是,天真地使用位摆弄 LFSR 执行此操作非常慢,因为您必须一次处理位。所以人们想出了使用预先计算的表格来一次八位甚至一次 32 位的方法。这就是为什么您几乎看不到 LFSR 代码的原因。在大多数生产代码中,它伪装成其他东西。

但有时一个简单的有点麻烦的 LFSR 可以派上用场。我曾经为 PIC micro 编写了一个 Modbus 驱动程序,该协议使用 CRC16。预先计算的表格需要 256 字节的内存,而我的 CPU 只有 68 字节( 我不是在开玩笑)。所以我不得不使用 LFSR。

原文由 slebetman 发布,翻译遵循 CC BY-SA 2.5 许可协议

由于我正在寻找 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 1 + a 0 x 0 | a 0 , a 1 , …, a 4 ∈ Z 2 }(澄清一下,Z 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) 描述了 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中的数字,因此执行模运算本身无非是通过将 P(x) 和 A i (x) 中的两个值加在一起来确定每个 a i变成 0 还是 1 (即,0+0=0、0+1=1、1+1=0,或“异或”这两个)。

每个多项式都可以写成一组位,例如x 4 +x 1 +x 0 ~ 10011。A 0 (x) 可以看作是种子。 ‘times x’ 操作可以看作是左移操作。模运算可以看作是位掩码操作,掩码就是我们的 P(x)。

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

  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)

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

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

通常,LFSR 的输出是“移出”的位,如果执行模运算则为“1”,否则为“0”。周期为 2 k-1 -1 的 LFSR,也称为伪噪声或 PN-LFSR,遵循 Golomb 的随机性假设,这说明这个输出位是“足够”随机的。

因此,这些位的序列在密码学中有其用途,例如在 A5/1 和 A5/2 移动加密标准或 E0 蓝牙标准中。然而,它们并不像人们希望的那样安全: Berlekamp-Massey 算法 可用于对 LFSR 的特征多项式(P(x))进行逆向工程。因此,强加密标准使用 Non-linear FSR 或类似的非线性函数。与此相关的主题是 AES 中使用的 S-Box


_请注意,我使用了 int.bit_length() 操作。这直到 Python 2.7 才实现。_

如果您只喜欢有限位模式,则可以检查种子是否等于结果,然后中断循环。

您可以在 for 循环中使用我的 LFSR 方法(例如 for xor, pattern in lfsr(0b001,0b10011) ),或者您可以重复调用 .next() 对方法结果的操作,返回一个新的 (xor, result) 每次配对。

原文由 ralphje 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题