Skip to content

Latest commit

 

History

History
206 lines (131 loc) · 8.71 KB

Question.md

File metadata and controls

206 lines (131 loc) · 8.71 KB

Question: Prime Number Efficiency Optimization

Difficulty Level: 🟡 Intermediate
Domain: Refactoring/Documentation

Description:
This task involves refactoring and documenting a Python program that checks for prime numbers within a given limit. The current implementation includes an inefficient primality check, poor variable naming conventions, and lacks proper documentation. You are required to improve the code's performance, readability, and maintainability by optimizing the algorithm, improving variable names, and adding comments and docstrings. You will also time the execution of the optimized version and compare it with the original.

Input Example:

get_primes(10000)

Output Example:

Total Prime Numbers Found: 1229
Time Taken: 0.154328 seconds

Original Code:

import time

def prime_check(n):
    if n <= 1:
        return False
    for j in range(2, n):  # Inefficient loop checking all numbers
        if n % j == 0:
            return False
    return True

def get_primes(lim):
    result = []  # Using a non-descriptive variable name
    for num in range(lim):  # Looping through all numbers
        if prime_check(num):  # Calling prime_check for every number
            result.append(num)  # Appending to result if prime
    return result  # Returning the results

# Timing the execution of the original code
start_t = time.time()  # Poor naming convention
primes_found = get_primes(10000)  # Finding primes up to 10,000
end_t = time.time()  # Poor naming convention

print(f"Total Prime Numbers Found: {len(primes_found)}")  # Non-informative output
print(f"Time Taken: {end_t - start_t:.6f} seconds")  # Non-informative output

Instructions:

  1. Refactor the Code by:

    • Improving the Prime-Checking Logic:

      • Modify the prime_check function to only check for divisors up to the square root of the number.
      • Implement more efficient algorithms, such as the Sieve of Eratosthenes, to reduce computational complexity.
    • Using More Descriptive Variable Names:

      • Rename variables like result, j, num, start_t, and end_t to more meaningful names that convey their purpose.
    • Optimizing Loops and Reducing Redundant Calculations:

      • Eliminate unnecessary iterations and computations within loops to enhance performance.
  2. Add Proper Documentation by:

    • Writing Docstrings for All Functions:

      • Provide clear and concise descriptions of each function’s purpose, parameters, and return values.
    • Commenting on the Purpose of Each Code Section and Explaining the Logic Used:

      • Add inline comments to explain complex sections of the code and the reasoning behind specific implementations.
  3. Include Execution Timing with Informative Output:

    • Clearly Explain the Performance Comparison Between the Original and Optimized Versions:
      • Display both the number of primes found and the time taken for execution in a user-friendly format.
      • Optionally, include a comparison between the original and refactored execution times to highlight performance improvements.
  4. Output the Number of Primes Found and the Time Taken in a Clean and User-Friendly Format:

    • Ensure that the printed output is easy to understand and provides meaningful information to the user.

Considerations:

  • Efficient Algorithms like the Sieve of Eratosthenes:

    • Consider implementing the Sieve of Eratosthenes for a more efficient prime number generation, which has a time complexity of O(n log log n).
  • Code Clarity and Readability:

    • Ensure that the refactored code is easy to read and understand, facilitating future maintenance and scalability.
  • Avoid Hard-Coding Values:

    • Replace hard-coded values (e.g., 10000) with variables or allow user input to make the code adaptable to different input sizes.

Submission Requirements:

  • Refactored Python Code (Solution.py):

    • Include the optimized and well-documented version of the original code.
    • Ensure that all functions have appropriate docstrings and inline comments.
  • Documentation (Explanation.md):

    • Provide a detailed explanation of the refactored code, highlighting the improvements made and the reasons behind them.
    • Explain the efficiency gains achieved through the refactoring process.

Sieve of Eratosthenes: Explanation

The Sieve of Eratosthenes is one of the most ancient and efficient algorithms devised for finding all prime numbers up to a specified integer limit. Named after the ancient Greek mathematician Eratosthenes, this algorithm systematically eliminates the multiples of each prime number, starting from 2, the smallest prime. Its simplicity and efficiency make it a fundamental tool in number theory and computer science for prime number generation.

How It Works

The Sieve of Eratosthenes operates by iteratively marking the multiples of each prime number starting from 2. The numbers which remain unmarked at the end of the process are prime numbers. Here's a step-by-step breakdown of the algorithm:

  1. Initialize a List:

    • Create a boolean list (sieve) of size n + 1 (where n is the upper limit) and initialize all entries as True. The index of the list represents the number itself.
    • Set the first two indices (0 and 1) to False since 0 and 1 are not prime numbers.
  2. Eliminate Non-Primes:

    • Start with the first prime number, 2.
    • Mark all multiples of 2 (except 2 itself) as False since they are not prime.
    • Move to the next number in the list that is still marked as True (the next prime) and repeat the process.
    • Continue this elimination process up to the square root of n. Any number remaining marked as True beyond this point is a prime number.
  3. Collect Prime Numbers:

    • After completing the elimination process, the indices of the list that remain marked as True correspond to prime numbers.

Visual Example

Let's visualize the Sieve of Eratosthenes with an example where n = 30:

  1. Initialize List:

    Index:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
    Value:  F  F  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T  T
    
    • F denotes False (not prime), and T denotes True (potential prime).
  2. Eliminate Multiples of 2:

    Index:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
    Value:  F  F  T  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F  T  F
    
    • All even numbers greater than 2 are marked as False.
  3. Eliminate Multiples of 3:

    Index:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
    Value:  F  F  T  T  F  T  F  T  F  F  F  T  F  T  F  F  F  T  F  T  F  F  F  T  F  T  F  F  F  T  F
    
    • Multiples of 3 greater than 3 are marked as False.
  4. Continue with Next Prime (5):

    • Since 5 * 5 = 25 is less than or equal to 30, we mark multiples of 5 starting from 5 * 5 = 25 as False.
    Index:  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
    Value:  F  F  T  T  F  T  F  T  F  F  F  T  F  T  F  F  F  T  F  T  F  F  F  T  F  F  F  F  F  T  F
    
  5. Final List of Primes Up to 30:

    Prime Numbers: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
    

Advantages of the Sieve of Eratosthenes

  1. Efficiency:

    • Time Complexity: The algorithm operates in O(n log log n) time, making it highly efficient for large values of n.
    • Space Complexity: It requires O(n) space to store the sieve list.
  2. Simplicity:

    • The algorithm is straightforward to understand and implement, making it accessible for educational purposes and practical applications.
  3. Scalability:

    • Suitable for generating all primes up to very large numbers, limited only by available memory.
  4. No Redundant Calculations:

    • By eliminating multiples in a systematic way, the algorithm avoids unnecessary computations, ensuring optimal performance.

Limitations

  1. Memory Consumption:

    • For extremely large values of n (e.g., billions), the memory required to store the sieve list can become prohibitive.
  2. Not Suitable for Dynamic Prime Queries:

    • The sieve is best suited for generating primes up to a known limit in advance. It is not ideal for checking the primality of individual numbers on-the-fly.