I ran into the concept of a Linear Feedback Shift Register so I wrote one:

#!/usr/bin/perl -w use strict; # Generate a closure that implements a Feedback Shift Register: # Usage: # my $fsr= genFSR( $bits, $seed, \&feedBack, [@taps] ); # my $nextBit= $fsr->(); sub genFSR { my( $bits, $seed, $code, $avTaps )= @_; return sub { my $ret= $seed & 1; my $b= $code->( map $seed & 1<<$_ ? 1 : 0, @$avTaps ); $seed >>= 1; $seed |= (1&$b)<<$bits; return $ret; }; } # XOR all arguments: sub xorList { my $b= 0; $b ^= pop while @_; return $b; } # Generate a Linear Feedback Shift Register closure: # Usage: # my $lfsr= genFSR( $bits, $seed, [@taps] ); # $nextBit= $lfsr->(); sub genLFSR { my( $bits, $seed, $avTaps )= @_; return genFSR( $bits, $seed, \&xorList, $avTaps ); } # Test the above using command-line arguments: # Usage: lfsr nBits Seed tap tap [tap [...]] # perl 5 10 3 5 # perl 5 0b1010 3 5 # Same if using perl 5.6 or later # Both of the above use: # genLFSR( 5, 10, [3,5] ); my $bits= shift; my $seed= shift; $seed= oct($seed) if $seed =~ /^0/; my $lfsr= genLFSR( $bits, $seed, [@ARGV] ); while( 1 ) { print join( " ", map $lfsr->(), 1..$bits ), $/; }

To make playing golf on this easier, you only have to implement a Linear FSR, and you don't have to accept the list of taps from an anonymous array.

So write a function that generates a closure that implements an N-bit LFSR with 2 or more taps:

my $lfsr= golfGenLFSR( $nBits, $iSeed, $tap0, $tap1, $tap2 ); my $nextBit= $lfsr->();
You may choose to use packed bit strings instead of integers (see vec).

        - tye (but my friends call me "Tye")

Replies are listed 'Best First'.
(tye)Re: LFSR golf
by tye (Sage) on May 08, 2001 at 09:09 UTC

    Here is a bit more on what an LFSR is.

    A LFSR is a register of N bits. Two or more positions are designated as "taps" and the bit values from those positions are XOR'd together to produce a new bit. This bit is placed to the left of the register and all bits are then shifted to the right with the previous right-most bit being returned as the output.

           4   3   2   1   0   <- tap location numbers
      ,->| 0 | 1 | 0 | 1 | 0 | -> Output
      |  +---+---+---+---+---+
      |        |           |
      |        |           |
      |        `->--XOR--<-'
      |              |

    You repeat this process forever, producing a somewhat random stream of bits as a result.

    A good LFSR will repeat the pattern of bits produced only after (2**N)-1 interations. For each size of LFSR, there are positions for the taps that result in such a "good" LFSR. Unless, of course, you seed it with a value of 0, which will always stay 0 since the XOR of any number of 0 bits is another 0 bit. Note, that is why the period can't be larger than (2**N)-1 since there are 2**N possible states and one of them (the value 0) can't be part of the loop.

    You can go to and search for LFSR to get more information on these. The person who pointed these out to me also pointed me to the book Applied Cryptography.

    From my quick introduction to these, I'm starting to think that most of the material on them is a bit sloppy. (:

    It seems to me that if you have an odd number of taps, then either a seed of 0 or of all 1 bits would never change (the XOR of an odd number of 1 bits is another 1 bit). But most of the "good" arrangements of taps have an odd number of taps [ which means that the maximum period for an odd number of taps would be (2**N)-2 ].

    So I think we should really be using XNOR instead of XOR. Though I'm reluctant to jump to many conclusions since I've just barely learned of them. :)

            - tye (but my friends call me "Tye")

      You repeat this process forever, producing a somewhat random stream of bits as a result.

      It might be better if you said a 'disordered' stream, or 'complicated', 'lacking pattern'

      The output is not random because it will be the same for the same initial setting of the register (like the 'random' number generator on most computers)

      I didn't believe in evil until I dated it.

        Like you mentioned, so-called "pseudorandom" number sequences are also identical for the same initial seed. I felt that "somewhat random" was less strong of a statement than "pseudorandom", but I can see where others would disagree. So I probably should have just stuck with "pseudorandom".

        For what it's worth, I find "disordered" and "lacking pattern" worse (the bits have a definite pattern and order). ;)

        This reminds me that I forgot to mention that the bit stream from a single LFSR has been studied to the point that a small subseries of bits is enough to predict the entire series. However, two carefully chosen LFSRs where each bit from one is used to determine whether the next bit from the other will be output or thrown away has been analyzed and found to be cryptographically strong.

                - tye (but my friends call me "Tye")
      Perhaps a more interesting challange would be an implementation of the Berlkamp-Massey algorithm. This algorithm capitalizes on knowing some series of bits produced by the LFSR (at least 2N bits, where N is the number of bits in the register) and determining what the taps are.
Re: LFSR golf
by Anonymous Monk on May 08, 2001 at 12:31 UTC
    How about
    sub l{my($s,$t)=@_;sub {($s=($s.(((my $x=$s+$t)=~y/1/1/d)%9%2)))=~s/5. +/5/;$s}} $x=l("50101","0022");print ($x->(),$x->(),$x->(),$x->());
    The first line is the generating sub (containing 79 chars) and the second one is an usage example.

      I don't mind the departure in how you call the generating function but I'm going to disqualify this one for having the closure return "50101" instead of 0 and "51100" instead of 1.

      If you were shifting the other direction, then a simple &1 at the end of the closure would solve the problem and give you a 73-character solution (not counting the "sub l{" and "}", as usual).

      I'm sure this general approach can be modified such that I'd accept it but I'll leave that to you or whoever beats you to it. (:

              - tye (but my friends call me "Tye")
        Didn't know you wanted that. OK. New version (72 chars)
        sub l{ my($s,$t)=@_;sub{($s=($s.(((my$x=$s+$t)=~y/1/1/d)%9%2)))=~s/5(.)/5/;$1 +} } $x=l("50101","0022");print ($x->(),$x->(),$x->(),$x->());