Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

A bit pattern can be converted to a list of bit positions set using grep:

bitpattern_2_indices { my $bit_pattern = shift; grep $bit_pattern & 1<<$_, 0..31; } my @indices = bitpattern_2_indices(12345); print "@indices"; # prints 0 3 4 5 12 13

Is there a more efficient way?

Can the range (0..31) be reduced by inspection?

Replies are listed 'Best First'.
Re: bit pattern to bit position mapping
by grinder (Bishop) on Nov 19, 2006 at 10:01 UTC

    You can use unpack to do most of the grunt work

    sub b2i { return split //, unpack('b*', @_); }
    This also obviates the need to determine whether it is a 32-bit or 64-bit quantity. You'll may get a number of zeros at the end of the array. You could clip the unnecessary zeros before splitting into values with:
    sub b2i { my $bitstring = unpack('b*', @_); $bitstring =~ s/0+$//; return split //, $bitstring; }

    • another intruder with the mooring in the heart of the Perl

      I'm after the positions of the set bits, not the bits themselves.

      Please see the comment in the code sample for what I mean.

        I'm after the positions of the set bits

        Ah so sorry, I left that as an exercise for the reader. All you have to do is run through the array of 0,1 values, and push the offset to another array if the value is 1. Then return that array. Which goes something like (untested)

        sub set_bit_offsets { my @bitmap = split //, unpack('b*', @_); my @offset; for my $off (0..$#bitmap) { if ($bitmap[$off] == 1) { push @offset, $off; } } return @offset; }

        In terms of efficiency, yes, that could be written as a one-liner. That would be something like:

        sub set_bit_offsets { my @bitmap = split //, unpack('b*', @_); return grep {$bitmap[$_]} 0..$#bitmap; }

        • another intruder with the mooring in the heart of the Perl

Re: bit pattern to bit position mapping
by jonadab (Parson) on Nov 19, 2006 at 13:36 UTC

    I take it you've profiled your application, which is experiencing noticeable performance issues, and determined that it's spending a lot of its time in this subroutine? Of course you have, and it's silly of me to even ask. Obviously you profiled your application, because you wouldn't otherwise be asking for a faster way to do this little thing, because you know full well that premature optimization is a root of all kinds of evil, for which some, having strayed, have pierced themselves through with many sorrows. You know this, and therefore you are only asking about a faster way to code this particular subroutine because you have established that its speed actually matters. Right? Right?


    Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. You can just call me "Mister Sanity". Why, I've got so much sanity it's driving me crazy.

      Yes. Yes. Yes!

      The application calls this routine 270 million times and it is currently consuming 14% of the total runtime.

      This isn't premature.

        it is currently consuming 14% of the total runtime

        oh, well why didn't you say so in the first place? Rewrite the hot function in C, and use array references, to stop slinging memory around.

        use strict; use Inline C => <<'__C_CODE__'; SV* b2i(int value) { AV *out; int i; out = (AV *)sv_2mortal((SV *)newAV()); i = 0; while (value > 0) { if (value & 1) av_push(out, newSViv(i)); ++i; value >>= 1; } return((SV *)newRV((SV *)out)); } __C_CODE__ my $ridx = b2i(12345); print "@$ridx\n";

        What does that do to the run time?

        • another intruder with the mooring in the heart of the Perl

        This isn't premature.

        I'd like to apologise for the tone of my earlier comment. Indeed, this does sound like it would be worth significant optimization effort.


        Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. You can just call me "Mister Sanity". Why, I've got so much sanity it's driving me crazy.

        s/270/27/.

Re: bit pattern to bit position mapping
by traveler (Parson) on Nov 19, 2006 at 17:06 UTC
    I suggest getting rid of the function code and inlining the grep. This saves the overhead of the function call, at least. You could use -P or your favorite macro processor to make it look like a function call.