How do you randomly zero a bit in an integer?

后端 未结 13 1916
無奈伤痛
無奈伤痛 2021-02-05 10:11

Updated with newer answer and better test

Let\'s say I have the number 382 which is 101111110.

How could I randomly turn a bit which is not 0 to

相关标签:
13条回答
  • 2021-02-05 10:47

    You can turn on any bit by OR'ing it with 1 and turn it off by AND'ing with the bitwise complement.

    Here's an example that selects a random 1-bit and turns it off.

    var rand = new Random();
    int myValue = 0x017E; // 101111110b
    // identify which indexes are one-bits (if any, thanks Doc)
    if( myValue > 0 )
    {
        var oneBitsIndexes = Enumerable.Range( 0, 31 )
                                       .Where(i => ((myValue >> i) & 0x1) !=0).ToList();
        // pick a random index and update the source value bit there from 1 to 0
        myValue &= ~(1 << oneBitsIndexes[rand.Next(oneBitsIndexes.Count)]);
    }
    // otherwise, there are no bits to turn off...
    
    0 讨论(0)
  • 2021-02-05 10:53

    Ok, a lot of wrong answers. Here's one that works:

    1. determine which bit to flip. Do this randomly. I won't supply the code, it's pretty straightforward.
    2. setup a bitmask with all zeros, with a 1 for the bit in question. So for example, if it's the 3rd bit, your bitmask might be 00000100. Again, this doesn't require code.
    3. bitwise XOR your number with the bit mask. If you're unfamiliar with the operator it's the hat operator: ^

    Here's some sample code:

    int myInt; // set this up with your original value
    int myBitmask; // set this up with the bit mask via steps 1 and 2.
    int randomlyZeroedBitInt = myInt ^ myBitmask;
    

    Edit: On a fifth read of the question, I have a question in return: you are wanting to do which of the following:

    1. Randomly zero a bit, but only if that bit is already 1. In other words, if the bit in question isn't already 1, the operation is a no-op.
    2. Randomly choose a bit that is 1 and zero it. This operation always chooses a bit that is already 1 and always zeros it. The operation is only a no-op if the original value is 0.

    Edit 2:

    2 is correct,(15chars) – Fredou

    In that case, my general algorithm stands; merely choose the bit in step 1 with internal logic. Alternatively, choose a fully random bit in step 1 and repeat until the value of myInt and randomlyZeroedBitInt are not equal.

    Unfortunately either case means a more complex algorithm, as you'll either need to iterate over every bit in your value to determine which to flip, or you'll need to loop the algorithm until a bit is flipped.

    0 讨论(0)
  • 2021-02-05 10:53

    Here is a version based on an algorithm from Bit Twiddling Hacks to select the nth set bit of an integer. For this case, we simply select n at random.

    The code has been ported to C#, made to work directly on 32-bit signed integers, and count from the right instead of the left. Furthermore, the optimization to remove all branches has not been preserved here as it yielded slower code on my machine (Intel Core 2 Quad Q9450).

    The description on the Bit Twiddling Hacks page does not give much insight into how the algorithm works. I have taken the time to step through and reverse engineer it and what I found is described in detail in the comments below.

    In my tests, this algorithm performs very similarly to Kyteland's excellent flipRandomBit over input that is distributed randomly across the full range of 32-bit integers. However, flipRandomBit is slightly faster for numbers with significantly fewer set bits than cleared bits. Conversely, this algorithm is slightly faster for numbers with significantly more set bits than cleared bits.

    The OP's benchmark consists entirely of small positive integers, which do not stress flipRandomBit's worst case. If this is an indication of the expected input, then all the more reason to prefer flipRandomBit.

    static int ClearRandomSetBit(int input) {
        ///////////////////////////////////////////////////////////////////////
        // ** Step 1 **
        // Count the set bits
        ////////////////////////////////////////////////////////////////////////
    
        // magic numbers
        const int m2 = 0x55555555; // 1 zero,  1 one,  ...
        const int m4 = 0x33333333; // 2 zeros, 2 ones, ...
        const int m8 = 0x0f0f0f0f; // 4 zeros, 4 ones, ...
    
        // sequence of 2-bit values representing the counts of each 2 bits.
        int c2 = input - ((input >> 1) & m2);
    
        // sequence of 4-bit values representing the counts of each 4 bits.
        int c4 = (c2 & m4) + ((c2 >> 2) & m4);
    
        // sequence of 8-bit values representing the counts of each 8 bits.
        int c8 = (c4 + (c4 >> 4)) & m8;
    
        // count set bits in input.
        int bitCount = (c8 * 0x1010101) >> 24;
    
        ///////////////////////////////////////////////////////////////////////////////////
        // ** Step 2 ** 
        // Select a random set bit to clear and find it using binary search with our 
        // knowledge of the bit counts in the various regions.
        ///////////////////////////////////////////////////////////////////////////////////
    
        // count 16 right-most bits where we'll begin our search
        int count = (c8 + (c8 >> 8)) & 0xff;
    
        // position of target bit among the set bits
        int target = random.Next(bitCount);
    
        // distance in set bits from the current position to the target
        int distance = target + 1;
    
        // current bit position 
        int pos = 0;
    
        // if the target is not in the right-most 16 bits, move past them
        if (distance > count) { pos += 16; distance -= count; }
    
        // if the target is not in the next 8 bits, move past them
        count = (c8 >> pos) & 0xff;
        if (distance > count) { pos += 8; distance -= count; }
    
        // if the target is not in the next 4 bits, move past them
        count = (c4 >> pos) & 0xf;
        if (distance > count) { pos += 4; distance -= count; }
    
        // if the target is not in the next 2 bits, move past them
        count = (c2 >> pos) & 0x3;
        if (distance > count) { pos += 2; distance -= count; }
    
        // if the bit is not the next bit, move past it.
        //
        // Note that distance and count must be single bits by now.
        // As such, distance is greater than count if and only if 
        // distance equals 1 and count equals 0. This obversation
        // allows us to optimize away the final branch.
        Debug.Assert((distance & 0x1) == distance);
        Debug.Assert((count & 0x1) == count);
        count = (input >> pos) & 0x1;
        pos += (distance & (count ^ 1));
    
        Debug.Assert((input & (1 << pos)) != 0);
        return input ^ (1 << pos);
    }
    
    0 讨论(0)
  • 2021-02-05 10:54
     int val=382
    
     int mask = ~(1 << N)   
    
     // this would turn-off nth bit (0 to 31)
     NewVal = (int) ((uint)val & (uint)mask} 
    
    0 讨论(0)
  • 2021-02-05 10:56
    static Random random = new Random();
    
    public static int Perturb(int data)
    {
        if (data == 0) return 0;
    
        // attempt to pick a more narrow search space
        int minBits = (data & 0xFFFF0000) == 0 ? 16 : 32;
    
        // int used = 0; // Uncomment for more-bounded performance
        int newData = data;
        do
        {
            // Unbounded performance guarantees
            newData &= ~(1 << random.Next(minBits));
    
            // // More-bounded performance:
            // int bit = 1 << random.Next(minBits);
            // if ((used & bit) == bit) continue;
            // used |= bit;
            // newData &= ~bit;
        } while (newData == data); // XXX: we know we've inverted at least one 1
                                   // when the new value differs
    
        return newData;
    }
    

    Update: added code above which can be used for more-bounded performance guarantees (or less unbounded if you want to think of it that way). Interestingly enough this performs better than the original uncommented version.

    Below is an alternate approach that is fast but without bounded performance guarantees:

    public static int FastPerturb(int data)
    {
        if (data == 0) return 0;
    
        int bit = 0;
        while (0 == (data & (bit = 1 << random.Next(32))));
    
        return data & ~bit;
    }
    
    0 讨论(0)
  • 2021-02-05 10:57

    Here's a slightly more efficient version using bit twiddling.

        public static int getBitCount(int bits)
        {
            bits = bits - ((bits >> 1) & 0x55555555);
            bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333);
            return ((bits + (bits >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
        }
    
        public static int flipRandomBit(int data)
        {
            int index = random.Next(getBitCount(data));
            int mask = data;
    
            for (int i = 0; i < index; i++)
                mask &= mask - 1;
            mask ^= mask & (mask - 1);
    
            return data ^ mask;
        }
    
    0 讨论(0)
提交回复
热议问题