Open In App

Randomized Algorithm in Python

Last Updated : 31 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Randomized algorithms are algorithms that use randomness as part of their logic to achieve efficiency and simplicity in solving computational problems. Unlike deterministic algorithms, which produce the same output for a given input, randomized algorithms may produce different outputs for the same input due to the involvement of randomness. These algorithms find applications in various domains, including computer science, cryptography, machine learning, and optimization.

What is Randomized Algorithm?

A randomized algorithm is an algorithm that uses random numbers or random choices during its execution to make decisions, in contrast to a deterministic algorithm that always follows the same set of steps.

Types of Randomized Algorithms:

Randomized algorithms can be broadly classified into two categories:

  1. Las Vegas Algorithms:
    • Las Vegas algorithms always produce the correct result, but their running time may vary depending on the random choices made during execution. These algorithms aim to optimize the average-case performance while maintaining correctness.
  2. Monte Carlo Algorithms:
    • Monte Carlo algorithms have a probabilistic guarantee of producing the correct result within a certain error margin. These algorithms prioritize efficiency and often sacrifice certainty for speed.

Example of Randomized Algorithms:

Let's explore some key examples of randomized algorithms:

1. Randomized QuickSort Algorithm

This sorting algorithm is widely used due to its efficiency. It works by randomly choosing a pivot element from the input array and partitioning the array around it. Elements smaller than the pivot are placed before it, and elements larger than the pivot are placed after it. This process is then recursively applied to the partitioned sub-arrays until the entire array is sorted. Quicksort has an average-case time complexity of O(n log n), making it significantly faster than many other sorting algorithms in practice.

Step-by-Step Approach:

  • Base Case: If the array has 1 or fewer elements, it's already sorted, so return the array as is.
  • Choose Pivot: Randomly select an element from the array as the pivot.
  • Partition: Create three sub-arrays: left containing elements less than the pivot, middle containing elements equal to the pivot, and right containing elements greater than the pivot.
  • Recursive Calls: Recursively call quicksort on the left and right sub-arrays. Combine the sorted left sub-array, the middle sub-array (which already contains the pivot in its sorted position), and the sorted right sub-array to form the final sorted array.

Below is the implementation of the above approach:

Python
def quicksort(arr):
    """
    Sorts an array using the randomized Quicksort algorithm.
    """

    # 1. Base case: If the array has 1 or fewer elements, it's already sorted.
    if len(arr) <= 1:
        return arr

    # 2. Choose a random pivot element from the array.
    pivot_index = random.randint(0, len(arr) - 1)
    pivot = arr[pivot_index]

    # 3. Partition the array around the pivot:
    #   - Create three sub-arrays: left (elements less than pivot),
    # middle (elements equal to pivot), and right (elements greater than pivot).
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]

    # 4. Recursively sort the left and right sub-arrays.
    return quicksort(left) + middle + quicksort(right)

2. Monte Carlo Algorithm for Approximate Counting

This technique uses randomness to estimate the probability of an event occurring or to approximate the value of a complex function. For example, it can be used to estimate the probability of winning a game or to calculate the integral of a function that is difficult to integrate analytically. Monte Carlo simulations are widely used in various fields, including finance, physics, and engineering.

Step-by-Step Approach:

  • Initialize Counters: Set up counters to track the number of points inside and outside the circle.
  • Run Trials: Perform the specified number of trials.
  • Generate Random Points: For each trial, generate random coordinates (x, y) within the unit square (0, 1).
  • Check Point Location: Determine if the generated point falls inside the unit circle using the equation x^2 + y^2 <= 1. If it does, increment the inside_circle counter.
  • Estimate Pi: After all trials, estimate pi using the ratio of points inside the circle to the total number of trials. This ratio approximates the area of the circle divided by the area of the square, which is equal to pi/4.

Below is the implementation of the above approach:

Python
def monte_carlo_pi(num_samples):

    # 1. Initialize counters for points inside and outside the circle.
    inside_circle = 0

    # 2. Perform the specified number of random trials.
    for _ in range(num_samples):
        # 3. Generate random coordinates (x, y) within the unit square.
        x = random.uniform(0, 1)
        y = random.uniform(0, 1)

        # 4. Check if the point is inside the unit circle (x^2 + y^2 <= 1).
        if x**2 + y**2 <= 1:
            inside_circle += 1

    # 5. Estimate pi using the ratio of points inside the circle
    # to the total number of trials.
    return 4 * inside_circle / num_samples

3. Las Vegas Primality Testing (Python)

Determining whether a large number is prime is a computationally challenging task. Randomized algorithms like the Miller-Rabin test offer a probabilistic approach to primality testing. These algorithms provide a high degree of confidence in the primality of a number without guaranteeing a definitive answer.

Step-by-Step Approach:

  • Special Cases: If n is less than or equal to 1, or if n is 4, return False. These are not prime numbers.
  • Finding d and s:
    • Initialize d to n - 1.
    • Initialize s to 0.
    • While d is even:
      • Divide d by 2.
      • Increment s by 1.
  • k Iterations: Loop k times (where k is the desired number of iterations for the test):
    • Generate a random integer a between 2 and n - 2.
    • Calculate x = pow(a, d, n) using modular exponentiation.
    • If x is 1 or n - 1, continue to the next iteration.
    • Otherwise, repeat the following s - 1 times:
      • Calculate x = pow(x, 2, n) using modular exponentiation.
      • If x becomes n - 1, break the inner loop and continue to the next iteration.
    • If x is not n - 1 after the inner loop, return False (the number is composite).
  • Probably Prime: If all k iterations complete without returning False, the number is considered "probably prime." Return True.

Below is the implementation of the above approach:

Python
def miller_rabin(n, k):
    """
    Performs the Miller-Rabin primality test on a number n with k iterations.
    """

    # 1. Handle special cases: 0, 1, 4 are not prime.
    if n <= 1 or n == 4:
        return False

    # 2. Find d and s such that n - 1 = 2^s * d (where d is odd).
    d = n - 1
    s = 0
    while d % 2 == 0:
        d //= 2
        s += 1

    # 3. Perform k iterations of the test.
    for _ in range(k):
        # 4. Choose a random integer a between 2 and n-2.
        a = random.randint(2, n - 2)

        # 5. Calculate x = a^d mod n.
        x = pow(a, d, n)

        # 6. Check if x is 1 or n-1:
        #   - If x is 1, the test is inconclusive.
        #   - If x is n-1, the test is inconclusive.
        if x == 1 or x == n - 1:
            continue

        # 7. Repeat steps 5 and 6 for s-1 times.
        for _ in range(s - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break

        # 8. If x is not n-1 after s-1 iterations, n is composite.
        if x != n - 1:
            return False

    # 9. If all iterations pass, n is probably prime.
    return True



Next Article
Practice Tags :

Similar Reads