Find Two Missing Numbers in an Array using Python

Introduction

In the world of computer science and programming, efficient algorithms are like hidden treasures. They allow us to solve complex problems with elegance and speed. One such intriguing problem is finding two missing numbers in an array. Given an array containing numbers from 1 to N, with two numbers missing, how can we efficiently identify these missing elements? This article explores a clever approach to solving this problem using bit manipulation in Python.

The Problem

Suppose we are given an array of N-2 distinct integers, where the integers range from 1 to N. Two numbers from this range have been removed from the array, causing the sequence to have two missing elements. Our task is to find these two missing numbers efficiently.

In this scenario, take the given array:

arr = {1, 3, 5, 6}

length = 6

Bit Manipulation Approach

Bit manipulation is a powerful technique that leverages the bitwise operations of the binary representation of numbers. To solve this problem using bit manipulation, we will take advantage of the XOR (exclusive OR) operation. XOR has a unique property that if we XOR two identical numbers, the result is 0.

For Example:

A XOR A = Zero

A XOR B = Non Zero

Solution

Step 1: Calculate XOR of all array elements

First, we need to calculate the XOR of all elements in the given array. This can be achieved by iterating through the array and performing XOR operations.

Applying this step to the given array:

x1 = {1 ^ 3 ^ 5 ^ 6}

x1 = 1

Step 2: Calculate XOR of all numbers from 1 to N

Next, we calculate the XOR of all integers from 1 to N.

x2 = {1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6}

x2 = 7

Step 3: Perform XOR the results obtained from steps 1 and 2

Now, we XOR the results obtained from steps 1 and 2.

x3 = x1 ^ x2

x3 = 6

= 0110 [In binary] 

Step 4: Identify the rightmost set bit

Find the rightmost set bit in the XOR obtained from step 3. This can be done by performing a bitwise AND operation with the two’s complement of the XOR value and the original XOR value. The result of this operation will isolate the rightmost set bit.

x3= 0110

position = 2

Step 5: Divide into two groups

Now, divide the elements of the given array and the numbers from 1 to N into two groups: one group (Group 1) with the rightmost set bit according to the position found in the step 4 and the other (Group 2) with the remaining numbers.

Binary Representation (Numbers where the rightmost set bit is highlighted in yellow)

1 = 0001

2 = 0010

3 = 0011

4 = 0100

5 = 0101

6 = 0110

Group 1

G1_Arr1 = {3, 6}

G1_Arr2 = {2, 3, 6}

Group 2

G2_Arr1 = {1, 5}

G2_Arr2 = {1, 4, 5}

Step 6: XOR within each group

XOR all the elements in each group separately. This will give us the two missing numbers.

First Missing Number

n1 = [ G1_Arr1 ] XOR [ G1_Arr2]

n1 = [3 ^ 6] ^ [2 ^ 3 ^ 6]

n1 = 2

Second Missing Number

n2 = [ G2_Arr1 ] XOR [ G2_Arr2]

n2 = [1 ^ 5] ^ [1 ^ 4 ^ 5]

n2 = 4

2 (n1) and 4 (n2) are the two missing numbers.

Python Implementation

Here’s the Python code that implements the above approach:


def find_missing_numbers(arr, N):
    xor_all = 0
    for num in arr:
        xor_all ^= num
    
    xor_nums = 0
    for num in range(1, N+1):
        xor_nums ^= num
    
    xor_combined = xor_all ^ xor_nums
    
    rightmost_set_bit = xor_combined & -xor_combined
    
    missing_num1 = 0
    missing_num2 = 0
    
    for num in arr:
        if num & rightmost_set_bit:
            missing_num1 ^= num
        else:
            missing_num2 ^= num
    
    for num in range(1, N+1):
        if num & rightmost_set_bit:
            missing_num1 ^= num
        else:
            missing_num2 ^= num
    
    return missing_num1, missing_num2

# Example usage
arr = [1, 3, 5, 6]
N = 6
missing1, missing2 = find_missing_numbers(arr, N)
print(f"Missing numbers: {missing1}, {missing2}")

Output

Missing numbers: 2, 4

Complexity Analysis

Let’s break down the time and space complexity of the given program:

Time Complexity

  1. Calculating XOR of all array elements: This step involves iterating through the entire array of size N-2 and performing an XOR operation on each element. This takes O(N) time.
  2. Calculating XOR of all numbers from 1 to N: This step involves iterating from 1 to N and performing XOR operations. Since there are N elements, this step also takes O(N) time.
  3. Calculating XOR of missing numbers: This step involves performing XOR on two integers, which can be done in O(1) time.
  4. Finding the rightmost set bit: Finding the rightmost set bit involves performing bitwise AND and some constant-time operations. This takes O(1) time.
  5. XOR operations within each group: These steps involve iterating through the array and numbers from 1 to N, performing XOR operations based on a condition. This step also takes O(N) time.

Overall, the dominant factor in the time complexity is the iteration through the array and numbers from 1 to N, which contributes to O(N) time complexity. Therefore, the total time complexity of the program is O(N).

Space Complexity

The space complexity of the program refers to the amount of additional memory used by the algorithm as the input size increases.

  1. `xor_all` and `xor_nums`: These variables are used to store the XOR values calculated from the array and the numbers from 1 to N. Each of these variables occupies constant space, so the space complexity is O(1).
  2. `xor_combined`, `rightmost_set_bit`, `missing_num1`, and `missing_num2`: These variables also occupy constant space, regardless of the input size, resulting in O(1) space complexity.
  3. Iteration variables and temporary variables: The program uses a few iteration variables and temporary variables that do not depend on the input size. Hence, they contribute to constant space complexity, O(1).

Overall, the space complexity of the program is O(1), as the memory usage remains constant regardless of the input size.

Conclusion

The bit manipulation approach outlined in this article provides an elegant solution to the problem of finding two missing numbers in an array. By leveraging the properties of XOR operations and the binary representation of numbers, we can efficiently identify the missing elements without requiring additional space. This technique not only showcases the power of bitwise operations but also highlights the beauty of algorithmic problem-solving in computer science.

Share your love
Subhankar Rakshit
Subhankar Rakshit

Hey there! I’m Subhankar Rakshit, the brains behind PySeek. I’m a Post Graduate in Computer Science. PySeek is where I channel my love for Python programming and share it with the world through engaging and informative blogs.

Articles: 194