线性反馈移位寄存器?
最近我反复接触到 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)
然后我突然想到,这在编写软件时有什么用?听说可以生成随机数;这是真的吗?如何? 因此,如果有人能够:
- 解释如何在软件开发中使用这样的设备
- ,并提出一些代码,以支持上述观点,或者就像我的那样,以任何语言展示不同的方法来实现这一点
另外,因为有关于这块逻辑和数字电路没有太多说教性的东西,如果这可以成为菜鸟(像我一样)更好地理解这个东西的地方,或者更好地了解这个东西,那就太好了了解它是什么以及它在编写软件时如何有用。应该将其设为社区维基吗?
也就是说,如果有人喜欢打高尔夫球……不客气。
Lately I bumped repeatedly into the concept of LFSR, that I find quite interesting because of its links with different fields and also fascinating in itself. It took me some effort to understand, the final help was this really good page, much better than the (at first) cryptic wikipedia entry. So I wanted to write some small code for a program that worked like a LFSR. To be more precise, that somehow showed how a LFSR works. Here's the cleanest thing I could come up with after some lenghtier attempts (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
I named "xor" the output of the XOR function, not very correct.
However, this is just meant to show how it circles through its possible states, in fact you noticed the register is represented by a string. Not much logical coherence.
This can be easily turned into a nice toy you can watch for hours (at least I could :-)
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)
Then it struck me, what use is this in writing software? I heard it can generate random numbers; is it true? how?
So, it would be nice if someone could:
- explain how to use such a device in software development
- come up with some code, to support the point above or just like mine to show different ways to do it, in any language
Also, as theres not much didactic stuff around about this piece of logic and digital circuitry, it would be nice if this could be a place for noobies (like me) to get a better understanding of this thing, or better, to understand what it is and how it can be useful when writing software. Should have made it a community wiki?
That said, if someone feels like golfing... you're welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
由于我正在寻找 Python 中的 LFSR 实现,所以我偶然发现了这个主题。然而,我发现根据我的需要,以下内容更准确:
上面的 LFSR 生成器基于 GF(2k) 模演算 (GF = 伽罗瓦域)。刚刚完成代数课程后,我将以数学方式解释这一点。
我们首先以 GF(24) 为例,它等于 {a4x4 + a3< /sub>x3 + a2x2 + a1x1 + a0x0 | a0, a1, ..., a4 ∈ Z2} (澄清一下,Z< sub>n = {0,1,...,n-1},因此 Z2 = {0,1},即一位)。这意味着这是所有四次多项式的集合,所有因子要么存在,要么不存在,但没有这些因子的倍数(例如,不存在 2xk)。 x3、x4 + x3、1 和 x4 + x3 > + x2 + x + 1 都是该组成员的示例。
我们将这组模取为四次多项式(即 P(x) ∈ GF(24)),例如 P(x) = x4+x 1+x0。这种对群的模运算也表示为 GF(24) / P(x)。作为参考,P(x) 描述了 LFSR 中的“抽头”。
我们还采用 3 次或更低的随机多项式(这样它就不会受到模数的影响,否则我们也可以直接对其执行模数运算),例如 A0(x) = x0。现在,每个后续的 Ai(x) 都是通过与 x 相乘来计算的: Ai(x) = Ai-1(x) * x 模 P(x)。
由于我们处于有限的范围内,模数运算可能会产生影响,但只有当生成的 Ai(x) 至少具有因子 x4(我们的最高P(x) 中的因子)。请注意,由于我们处理的是 Z2 中的数字,因此执行模运算本身无非就是通过将两个 ai 相加来确定每个 ai 是否变为 0 或 1 P(x) 和 Ai(x) 的值在一起(即,0+0=0、0+1=1、1+1=0,或“异或”这两个值)。
每个多项式都可以写成一组位,例如 x4+x1+x0 ~ 10011。 0(x)可以看作种子。 “乘x”运算可以看作左移运算。模运算可以看作是位掩码运算,掩码就是我们的 P(x)。
因此,上述算法生成(无限流)有效的四位 LFSR 模式。例如,对于我们定义的 A0(x) (x0) 和 P(x) (x4 +x1+x0),我们可以在 GF(24 中定义以下第一个产生的结果) (请注意,A0 直到第一轮结束时才会产生 - 数学家通常从“1”开始计数):
请注意,您的掩码必须在第四个位置包含“1”以确保您的 LFSR 生成四位结果。另请注意,“1”必须出现在第 0 个位置,以确保您的比特流不会以 0000 位模式结束,或者最后一位将变为未使用(如果所有位都向左移动,您将一次转换后,第 0 个位置处也会有一个 0)。
并非所有 P(x) 都一定是 GF(2k) 的生成器(即,并非所有 k 位掩码都生成所有 2k-1-1 数字) 。例如,x4 + x3 + x2 + x1 + x0 support> 生成 3 组,每组 5 个不同的多项式,或“周期 5 的 3 个周期”:0001,0010,0100,1000,1111; 0011,0110,1100,0111,1110;和0101、1010、1011、1001、1101。请注意,永远无法生成 0000,也无法生成任何其他数字。
通常,LFSR 的输出是“移出”的位,如果执行模运算,则为“1”;如果未执行模运算,则为“0”。周期为 2k-1-1 的 LFSR(也称为伪噪声或 PN-LFSR)遵循哥伦布随机性假设,该假设表明该输出位“足够”随机。
因此,这些位的序列在密码学中具有用途,例如在 A5/1 和 A5/2 移动加密标准或 E0 蓝牙标准中。然而,它们并不像人们想象的那么安全:Berlekamp-Massey 算法 可用于对 LFSR 的特征多项式 (P(x)) 进行逆向工程。因此,强加密标准使用非线性 FSR 或类似的非线性函数。与此相关的主题是 AES 中使用的 S-Box。
请注意,我使用了
int.bit_length()
操作。这直到 Python 2.7 才实现。如果您只想要有限位模式,您可以检查种子是否等于结果,然后中断循环。
您可以在 for 循环中使用我的 LFSR 方法(例如
for xor,pattern in lfsr(0b001,0b10011)
),或者您可以重复调用.next()
对方法结果进行操作,每次都返回一个新的(xor, result)
-对。Since I was looking for a LFSR-implementation in Python, I stumbled upon this topic. I found however that the following was a bit more accurate according to my needs:
The above LFSR-generator is based on GF(2k) modulus calculus (GF = Galois Field). Having just completed an Algebra course, I'm going to explain this the mathematical way.
Let's start by taking, for example, GF(24), which equals to {a4x4 + a3x3 + a2x2 + a1x1 + a0x0 | a0, a1, ..., a4 ∈ Z2} (to clarify, Zn = {0,1,...,n-1} and therefore Z2 = {0,1}, i.e. one bit). This means that this is the set of all polynomials of the fourth degree with all factors either being present or not, but having no multiples of these factors (e.g. there's no 2xk). x3, x4 + x3, 1 and x4 + x3 + x2 + x + 1 are all examples of members of this group.
We take this set modulus a polynomial of the fourth degree (i.e., P(x) ∈ GF(24)), e.g. P(x) = x4+x1+x0. This modulus operation on a group is also denoted as GF(24) / P(x). For your reference, P(x) describes the 'taps' within the LFSR.
We also take a random polynomial of degree 3 or lower (so that it's not affected by our modulus, otherwise we could just as well perform the modulus operation directly on it), e.g. A0(x) = x0. Now every subsequent Ai(x) is calculated by multiplying it with x: Ai(x) = Ai-1(x) * x mod P(x).
Since we are in a limited field, the modulus operation may have an effect, but only when the resulting Ai(x) has at least a factor x4 (our highest factor in P(x)). Note that, since we are working with numbers in Z2, performing the modulus operation itself is nothing more than determining whether every ai becomes a 0 or 1 by adding the two values from P(x) and Ai(x) together (i.e., 0+0=0, 0+1=1, 1+1=0, or 'xoring' these two).
Every polynomial can be written as a set of bits, for example x4+x1+x0 ~ 10011. The A0(x) can be seen as the seed. The 'times x' operation can be seen as a shift left operation. The modulus operation can be seen as a bit masking operation, with the mask being our P(x).
The algorithm depicted above therefore generates (an infinite stream of) valid four bit LFSR patterns. For example, for our defined A0(x) (x0) and P(x) (x4+x1+x0), we can define the following first yielded results in GF(24) (note that A0 is not yielded until at the end of the first round -- mathematicians generally start counting at '1'):
Note that your mask must contain a '1' at the fourth position to make sure that your LFSR generates four-bit results. Also note that a '1' must be present at the zeroth position to make sure that your bitstream would not end up with a 0000 bit pattern, or that the final bit would become unused (if all bits are shifted to the left, you would also end up with a zero at the 0th position after one shift).
Not all P(x)'s necessarily are generators for GF(2k) (i.e., not all masks of k bits generate all 2k-1-1 numbers). For example, x4 + x3 + x2 + x1 + x0 generates 3 groups of 5 distinct polynomals each, or "3 cycles of period 5": 0001,0010,0100,1000,1111; 0011,0110,1100,0111,1110; and 0101,1010,1011,1001,1101. Note that 0000 can never be generated, and can't generate any other number.
Usually, the output of an LFSR is the bit that is 'shifted' out, which is a '1' if the modulus operation is performed, and a '0' when it isn't. LFSR's with a period of 2k-1-1, also called pseudo-noise or PN-LFSR's, adhere to Golomb's randomness postulates, which says as much as that this output bit is random 'enough'.
Sequences of these bits therefore have their use in cryptography, for instance in the A5/1 and A5/2 mobile encryption standards, or the E0 Bluetooth standard. However, they are not as secure as one would like: the Berlekamp-Massey algorithm can be used to reverse-engineer the characteristic polynomal (the P(x)) of the LFSR. Strong encryption standards therefore use Non-linear FSR's or similar non-linear functions. A related topic to this are the S-Boxes used in AES.
Note that I have used the
int.bit_length()
operation. This was not implemented until Python 2.7.If you'd only like a finite bit pattern, you could check whether the seed equals the result and then break your loop.
You can use my LFSR-method in a for-loop (e.g.
for xor, pattern in lfsr(0b001,0b10011)
) or you can repeatedly call the.next()
operation on the result of the method, returning a new(xor, result)
-pair everytime.实际上,基于 LFSR 的算法很常见。 CRC实际上是直接基于LFSR的。当然,在计算机科学课程中,当人们谈论输入值如何与累积值进行异或时,人们会谈论多项式,而在电子工程中,我们谈论的是抽头。它们是相同的,只是术语不同。
CRC32 是一种非常常见的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 执行此操作非常慢,因为您必须一次处理位。因此,人们想出了使用预先计算表一次执行 8 位甚至一次 32 位的方法。这就是为什么你几乎不会在野外看到 LFSR 代码。在大多数生产代码中,它伪装成其他东西。
但有时简单的 LFSR 会派上用场。我曾经为 PIC micro 编写过 Modbus 驱动程序,该协议使用 CRC16。预先计算的表需要 256 字节内存,而我的 CPU 只有 68 字节(我不是在开玩笑)。所以我不得不使用 LFSR。
Actually, algorithms based on LFSR are very common. CRC is actually directly based on LFSR. Of course, in computer science classes people talk about polynomials when they're talking about how the input value is supposed to be XORed with the accumulated value, in electornics engineering we talk about taps instead. They are the same just different terminology.
CRC32 is a very common one. It's used to detect errors in Ethernet frames. That means that when I posted this answer my PC used an LFSR based algorithm to generate a hash of the IP packet so that my router can verify that what it's transmitting isn't corrupted.
Zip and Gzip files are another example. Both use CRC for error detection. Zip uses CRC32 and Gzip uses both CRC16 and CRC32.
CRCs are basically hash functions. And it's good enough to make the internet work. Which means LFSRs are fairly good hash functions. I'm not sure if you know this but in general good hash functions are considered good random number generators. But the thing with LFSR is that selecting the correct taps (polynomials) is very important to the quality of the hash/random number.
Your code is generally toy code since it operates on a string of ones and zeros. In the real world LFSR work on bits in a byte. Each byte you push through the LFSR changes the accumulated value of the register. That value is effectively a checksum of all the bytes you've push through the register. Two common ways of using that value as a random number is to either use a counter and push a sequence of numbers through the register, thereby transforming the linear sequence 1,2,3,4 to some hashed sequence like 15306,22,5587,994, or to feed back the current value into the register to generate a new number in seemingly random sequence.
It should be noted that doing this naively using bit-fiddling LFSR is quite slow since you have to process bits at a time. So people have come up with ways using pre-calculated tables to do it eight bits at a time or even 32 bits at a time. This is why you almost never see LFSR code in the wild. In most production code it masquerades as something else.
But sometimes a plain bit-twiddling LFSR can come in handy. I once wrote a Modbus driver for a PIC micro and that protocol used CRC16. A pre-calculated table requires 256 bytes of memory and my CPU only had 68 bytes (I'm not kidding). So I had to use an LFSR.
LFSR 有很多应用。其中之一是产生噪音,例如 SN76489 及其变体(用于 Master System、Game Gear、MegaDrive、NeoGeo Pocket 等)使用 LFSR 来产生白/周期性噪音。 本页对 SN76489 的 LFSR 进行了很好的描述。
There are many applications of LFSRs. One of them is generating noise, for instance the SN76489 and variants (used on the Master System, Game Gear, MegaDrive, NeoGeo Pocket, ...) use a LFSR to generate white/periodic noise. There's a really good description of SN76489's LFSR in this page.
这是我的 python 库之一 - pylfsr 来实现 LFSR。我试图使其高效,可以处理任何长度的 LFSR 来生成二进制序列。
您也可以在步骤中显示所有信息,
输出
也可以像这样可视化
查看 此处的文档
Here is one of my python libraries - pylfsr to implement LFSR. I have tried to make it an efficient that can handle any length of LFSR to generate the binary sequence.
You can display all the info at step too,
Output
Also can be visualize like this
Check out the Documentation here
为了使其真正优雅和 Python 化,请尝试创建一个生成器,从 LFSR 生成连续值。此外,与浮点
0.0
进行比较是不必要的且令人困惑。LFSR 只是在计算机中创建伪随机数的多种方法之一。伪随机,因为这些数字并不是真正随机的 - 您可以通过从种子(初始值)开始并继续进行相同的数学运算来轻松重复它们。
To make it really elegant and Pythonic, try to create a generator,
yield
-ing successive values from the LFSR. Also, comparing to a floating point0.0
is unnecessary and confusing.A LFSR is just one of many ways to create pseudo-random numbers in computers. Pseudo-random, because there numbers aren't really random - you can easily repeat them by starting with the seed (initial value) and proceeding with the same mathematical operations.
下面是使用整数和二元运算符而不是字符串的代码变体。正如有人建议的那样,它还使用了产量。
Below is a variation on your code using integers and binary operators instead of strings. It also uses yield as someone suggested.
这里有一段代码,您可以在其中选择您的种子、位数和您想要的抽头:
Here a piece of code where you can choose your seed, the number of bits and the taps you want:
如果我们假设种子是一个整数列表而不是一个字符串(如果不是字符串则将其转换),那么下面的代码应该更优雅地完成您想要的操作:
示例:
If we assume that seed is a list of ints rather than a string (or convert it if it is not) then the following should do what you want with a bit more elegance:
Example :
此类提供了一个易于使用的 LFSR 生成器对象
This class provides an easy of use LFSR generator object