mirror of
https://github.com/TheAlgorithms/Java.git
synced 2026-03-13 08:40:43 +08:00
Added CountBitsFlip algo (#6603)
* Added CountBitsFlip aldo * checkstyle fix * checkstyle fix --------- Co-authored-by: Alx <alexanderklmn@gmail.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package com.thealgorithms.bitmanipulation;
|
||||
|
||||
/**
|
||||
* Implementation to count number of bits to be flipped to convert A to B
|
||||
*
|
||||
* Problem: Given two numbers A and B, count the number of bits needed to be
|
||||
* flipped to convert A to B.
|
||||
*
|
||||
* Example:
|
||||
* A = 10 (01010 in binary)
|
||||
* B = 20 (10100 in binary)
|
||||
* XOR = 30 (11110 in binary) - positions where bits differ
|
||||
* Answer: 4 bits need to be flipped
|
||||
*
|
||||
* Time Complexity: O(log n) - where n is the number of set bits
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
*@author [Yash Rajput](https://github.com/the-yash-rajput)
|
||||
*/
|
||||
public final class CountBitsFlip {
|
||||
|
||||
private CountBitsFlip() {
|
||||
throw new AssertionError("No instances.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of bits that need to be flipped to convert a to b
|
||||
*
|
||||
* Algorithm:
|
||||
* 1. XOR a and b to get positions where bits differ
|
||||
* 2. Count the number of set bits in the XOR result
|
||||
* 3. Use Brian Kernighan's algorithm: n & (n-1) removes rightmost set bit
|
||||
*
|
||||
* @param a the source number
|
||||
* @param b the target number
|
||||
* @return the number of bits to flip to convert A to B
|
||||
*/
|
||||
public static long countBitsFlip(long a, long b) {
|
||||
int count = 0;
|
||||
|
||||
// XOR gives us positions where bits differ
|
||||
long xorResult = a ^ b;
|
||||
|
||||
// Count set bits using Brian Kernighan's algorithm
|
||||
while (xorResult != 0) {
|
||||
xorResult = xorResult & (xorResult - 1); // Remove rightmost set bit
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative implementation using Long.bitCount().
|
||||
*
|
||||
* @param a the source number
|
||||
* @param b the target number
|
||||
* @return the number of bits to flip to convert a to b
|
||||
*/
|
||||
public static long countBitsFlipAlternative(long a, long b) {
|
||||
return Long.bitCount(a ^ b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.thealgorithms.bitmanipulation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Random;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for CountBitsFlip.
|
||||
* Covers:
|
||||
* - simple examples
|
||||
* - zeros and identical values
|
||||
* - negative numbers and two's complement edge cases
|
||||
* - Long.MIN_VALUE / Long.MAX_VALUE
|
||||
* - randomized consistency checks between two implementations
|
||||
*/
|
||||
@DisplayName("CountBitsFlip Tests")
|
||||
class CountBitsFlipTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Example: A=10, B=20 => 4 bits")
|
||||
void exampleTenTwenty() {
|
||||
long a = 10L;
|
||||
long b = 20L;
|
||||
long expected = 4L;
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlip(a, b), "Brian Kernighan implementation should return 4");
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlipAlternative(a, b), "Long.bitCount implementation should return 4");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Identical values => 0 bits")
|
||||
void identicalValues() {
|
||||
long a = 123456789L;
|
||||
long b = 123456789L;
|
||||
long expected = 0L;
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlip(a, b));
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlipAlternative(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Both zeros => 0 bits")
|
||||
void bothZeros() {
|
||||
assertEquals(0L, CountBitsFlip.countBitsFlip(0L, 0L));
|
||||
assertEquals(0L, CountBitsFlip.countBitsFlipAlternative(0L, 0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Small example: A=15 (1111), B=8 (1000) => 3 bits")
|
||||
void smallExample() {
|
||||
long a = 15L; // 1111
|
||||
long b = 8L; // 1000
|
||||
long expected = 3L; // differs in three low bits
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlip(a, b));
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlipAlternative(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Negative values: -1 vs 0 => 64 bits (two's complement all ones)")
|
||||
void negativeVsZero() {
|
||||
long a = -1L;
|
||||
long b = 0L;
|
||||
long expected = 64L; // all 64 bits differ
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlip(a, b));
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlipAlternative(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Long.MIN_VALUE vs Long.MAX_VALUE => 64 bits")
|
||||
void minMaxLongs() {
|
||||
long a = Long.MIN_VALUE;
|
||||
long b = Long.MAX_VALUE;
|
||||
long expected = 64L; // MAX ^ MIN yields all ones on 64-bit long
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlip(a, b));
|
||||
assertEquals(expected, CountBitsFlip.countBitsFlipAlternative(a, b));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Randomized consistency: both implementations agree across many pairs")
|
||||
void randomizedConsistency() {
|
||||
final int iterations = 1000;
|
||||
final Random rnd = new Random(12345L); // deterministic seed for reproducibility
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
long a = rnd.nextLong();
|
||||
long b = rnd.nextLong();
|
||||
|
||||
long res1 = CountBitsFlip.countBitsFlip(a, b);
|
||||
long res2 = CountBitsFlip.countBitsFlipAlternative(a, b);
|
||||
|
||||
assertEquals(res2, res1, () -> String.format("Mismatch for a=%d, b=%d: impl1=%d, impl2=%d", a, b, res1, res2));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user