Skip to content

Commit 585e849

Browse files
authored
Merge pull request #5 from eliasailenei/solution2
Solution2
2 parents 8618979 + 9b53900 commit 585e849

File tree

4 files changed

+95
-74
lines changed

4 files changed

+95
-74
lines changed

__pycache__/sol2.cpython-313.pyc

313 Bytes
Binary file not shown.

__pycache__/sol3.cpython-313.pyc

0 Bytes
Binary file not shown.

graph.py

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,40 @@
22
import matplotlib.pyplot as plt
33
import sol2 # Assuming sol2 is provided
44
import sol1 # Assuming sol1 is provided
5-
import sol3 # Since sol3 is nearly identical to sol2, we alias it for clarity
5+
import sol3 # Assuming sol3 is provided and similar to sol2
6+
7+
def run_case(binary_str, n, solution, header):
8+
start_time = time.perf_counter_ns()
9+
output_sol = solution.extract_primes(binary_str, n)
10+
elapsed_time_ns = time.perf_counter_ns() - start_time
11+
12+
if elapsed_time_ns < 1_000: # Less than 1µs
13+
formatted_time = f"{elapsed_time_ns} ns"
14+
elif elapsed_time_ns < 1_000_000: # Less than 1ms
15+
formatted_time = f"{elapsed_time_ns / 1_000:.3f} µs"
16+
elif elapsed_time_ns < 1_000_000_000: # Less than 1s
17+
formatted_time = f"{elapsed_time_ns / 1_000_000:.3f} ms"
18+
else:
19+
formatted_time = f"{elapsed_time_ns / 1_000_000_000:.6f} s"
20+
21+
elapsed_time_s = elapsed_time_ns / 1_000_000_000 # Convert to seconds
22+
return f" {header}: \n Time taken: {formatted_time} \n Answer: {output_sol}", elapsed_time_s
23+
24+
# Define test cases
625
test_cases = [
7-
("0100001101001111", 999999), # 1
8-
("01000011010011110100110101010000", 999999), # 2
9-
("1111111111111111111111111111111111111111", 999999), # 3
10-
("010000110100111101001101010100000011000100111000", 999999999), # 4
11-
("01000011010011110100110101010000001100010011100000110001", 123456789012), # 5
12-
("0100001101001111010011010101000000110001001110000011000100111001", 123456789012345), # 6
13-
("010000110100111101001101010100000011000100111000001100010011100100100001", 123456789012345678), # 7
14-
("01000011010011110100110101010000001100010011100000110001001110010010000101000001", 1234567890123456789), # 8
15-
("0100001101001111010011010101000000110001001110000011000100111001001000010100000101000100", 1234567890123456789), # 9
16-
("010000110100111101001101010100000011000100111000001100010011100100100001010000010100010001010011", 12345678901234567890) # 10
26+
("0100001101001111", 999999),
27+
("01000011010011110100110101010000", 999999),
28+
("1111111111111111111111111111111111111111", 999999),
29+
("010000110100111101001101010100000011000100111000", 999999999),
30+
("01000011010011110100110101010000001100010011100000110001", 123456789012),
31+
("0100001101001111010011010101000000110001001110000011000100111001", 123456789012345),
32+
("010000110100111101001101010100000011000100111000001100010011100100100001", 123456789012345678),
33+
("01000011010011110100110101010000001100010011100000110001001110010010000101000001", 1234567890123456789),
34+
("0100001101001111010011010101000000110001001110000011000100111001001000010100000101000100", 1234567890123456789),
35+
("010000110100111101001101010100000011000100111000001100010011100100100001010000010100010001010011", 12345678901234567890),
1736
]
1837

19-
# Store execution times and results for each solution
38+
# Store execution times
2039
execution_times_sol1 = []
2140
execution_times_sol2 = []
2241
execution_times_sol3 = []
@@ -25,41 +44,42 @@
2544
# Run performance tests
2645
for idx, (binary_str, n) in enumerate(test_cases):
2746
print(f"Test Case {idx + 1}")
47+
48+
result, time_sol1 = run_case(binary_str, n, sol1, "Solution 1")
49+
execution_times_sol1.append(time_sol1)
50+
print(result)
2851

29-
# Solution 1
30-
start_time = time.time()
31-
output_sol1 = sol1.extract_primes(binary_str, n)
32-
elapsed_time_sol1 = time.time() - start_time
33-
execution_times_sol1.append(elapsed_time_sol1)
34-
print(f"Solution 1 - trail only:\n Time taken: {elapsed_time_sol1:.6f}s\n Answer: {output_sol1}")
52+
result, time_sol2 = run_case(binary_str, n, sol2, "Solution 2")
53+
execution_times_sol2.append(time_sol2)
54+
print(result)
3555

36-
# Solution 2
37-
start_time = time.time()
38-
output_sol2 = sol2.extract_primes(binary_str, n)
39-
elapsed_time_sol2 = time.time() - start_time
40-
execution_times_sol2.append(elapsed_time_sol2)
41-
print(f"Solution 2 -sieve only:\n Time taken: {elapsed_time_sol2:.6f}s\n Answer: {output_sol2}")
42-
43-
# Solution2 - old version BAD
44-
start_time = time.time()
45-
output_sol3 = sol3.extract_primes(binary_str, n)
46-
elapsed_time_sol3 = time.time() - start_time
47-
execution_times_sol3.append(elapsed_time_sol3)
48-
print(f"Solution 2- the holy trinity:\n Time taken: {elapsed_time_sol3:.6f}s\n Answer: {output_sol3}\n")
56+
result, time_sol3 = run_case(binary_str, n, sol3, "Solution 2 - holy trinity")
57+
execution_times_sol3.append(time_sol3)
58+
print(result)
4959

5060
# Plot execution times
5161
plt.figure(figsize=(10, 5))
52-
plt.plot(test_case_indices, execution_times_sol1, marker='o', linestyle='-', label="Solution 1- trial only")
53-
plt.plot(test_case_indices, execution_times_sol2, marker='s', linestyle='--', label="Solution 2 - sieve only")
54-
plt.plot(test_case_indices, execution_times_sol3, marker='d', linestyle='-.', label="Solution 2- the holy trinity")
62+
plt.plot(test_case_indices, execution_times_sol1, marker='o', linestyle='-', label="Solution 1")
63+
plt.plot(test_case_indices, execution_times_sol2, marker='s', linestyle='--', label="Solution 2")
64+
plt.plot(test_case_indices, execution_times_sol3, marker='d', linestyle='-.', label="Solution 2 - the holy trinity")
65+
66+
plt.xlabel("Test Case Number")
67+
plt.ylabel("Execution Time (s)")
68+
plt.title("Execution Time Comparison: Solution 1, 2, and 3")
69+
plt.xticks(test_case_indices)
70+
plt.grid(True)
71+
plt.legend()
72+
plt.show()
73+
74+
# Plot execution times for only Solution 2 - holy trinity
75+
plt.figure(figsize=(10, 5))
76+
plt.plot(test_case_indices, execution_times_sol3, marker='d', linestyle='-.', color='r', label="Solution 2 - the holy trinity")
5577

56-
# Labels and title
5778
plt.xlabel("Test Case Number")
5879
plt.ylabel("Execution Time (s)")
59-
plt.title("Performance of extract_primes Function Across Test Cases")
80+
plt.title("Execution Time: Solution 2 - The Holy Trinity")
6081
plt.xticks(test_case_indices)
6182
plt.grid(True)
6283
plt.legend()
84+
plt.show()
6385

64-
# Show the plot
65-
plt.show()

sol3.py

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,59 @@
1-
import math,random
2-
def sieve_segment(low, high):
3-
sieve_range = high - low + 1
4-
is_prime = [True] * sieve_range
1+
import math,random # built-in libs, allowed by spec
2+
def sieve_segment(low, high): # sieve is going to gen numbers from x to y instead of 1 to n
3+
sieve_range = high - low + 1
4+
is_prime = [True] * sieve_range # this will set all numbers to prime
55
if low == 1: is_prime[0] = False
66
limit = int(math.sqrt(high))
7-
small_primes = sieve_upto(limit)[1]
8-
for prime in small_primes:
9-
start = max(prime * prime, low + (prime - low % prime) % prime)
10-
for j in range(start, high + 1, prime):
7+
small_primes = sieve_upto(limit)[1] # this will get the prime numbers
8+
for prime in small_primes: # this will iterate through the prime numbers
9+
start = max(prime * prime, low + (prime - low % prime) % prime) # this will get the start of the prime number
10+
for j in range(start, high + 1, prime):
1111
is_prime[j - low] = False
12-
return is_prime, [low + i for i in range(sieve_range) if is_prime[i]]
13-
def sieve_upto(n):
12+
return is_prime, [low + i for i in range(sieve_range) if is_prime[i]] # this will return the prime numbers
13+
def sieve_upto(n): # the main sieve function
1414
is_prime = [True] * (n + 1)
1515
is_prime[0] = is_prime[1] = False
1616
for i in range(2, int(n ** 0.5) + 1):
1717
if is_prime[i]:
1818
for j in range(i * i, n + 1, i):
1919
is_prime[j] = False
2020
return is_prime, [x for x in range(2, n + 1) if is_prime[x]]
21-
def is_prime_trial(n):
21+
def is_prime_trial(n): # this is a simple trial division method to check if the number is prime
2222
if n < 2: return False
2323
if n in (2, 3): return True
2424
if n % 2 == 0 or n % 3 == 0: return False
25-
limit = int(math.sqrt(n))
25+
limit = int(math.sqrt(n)) # this will get the square root of the number and set the limit
2626
i = 5
2727
while i <= limit:
2828
if n % i == 0 or n % (i + 2) == 0: return False
2929
i += 6
3030
return True
31-
def is_prime_fermat(n, k=5):
31+
def is_prime_fermat(n, k=5): # this is the fermat primality test to check if the number is prime
3232
if n < 2: return False
3333
if n in (2, 3): return True
3434
if n % 2 == 0 or n % 3 == 0: return False
35-
for _ in range(k):
35+
for _ in range(k): # this will iterate through the number
3636
a = random.randint(2, n - 2)
3737
if pow(a, n - 1, n) != 1: return False
3838
return True
39-
def pollards_rho(n):
39+
def pollards_rho(n): # this is the pollards rho algorithm to check if the number is prime
4040
if n % 2 == 0: return 2
4141
x = random.randint(2, n - 1); y = x; c = random.randint(1, n - 1); d = 1
4242
def f(x): return (x * x + c) % n
43-
while d == 1: x = f(x); y = f(f(y)); d = math.gcd(abs(x - y), n)
43+
while d == 1: x = f(x); y = f(f(y)); d = math.gcd(abs(x - y), n) # using the greatest common divisor to check if the number is prime
4444
return d if d != n else None
45-
def is_prime_large(n):
45+
def is_prime_large(n): # this is the main function to check if the number is prime for a large N
4646
if is_prime_fermat(n): return True
4747
factor = pollards_rho(n)
4848
if factor and factor != n: return False
4949
return True
50-
def extract_primes(binary_str, N):
51-
if not all(index in "01" for index in binary_str): return "0: Invalid binary strings"
52-
if type(N) is not int: return "0: Invalid N given"
53-
candidates = set()
50+
def extract_primes(binary_str, N): # the main function
51+
if not all(index in "01" for index in binary_str): return "0: Invalid binary strings" # edge case : only 0 and 1 are allowed
52+
if type(N) is not int: return "0: Invalid N given" # edge case : N must be an integer
53+
candidates = set() # no dupes allowed
5454
length = len(binary_str)
5555
prefix = [0] * (length + 1)
56-
for i in range(length):
56+
for i in range(length): # this will convert the binary string to a decimal number
5757
prefix[i+1] = (prefix[i] << 1) + (binary_str[i] == '1')
5858
for start in range(length):
5959
for end in range(start + 1, length + 1):
@@ -62,25 +62,25 @@ def extract_primes(binary_str, N):
6262
if val > 1: candidates.add(val)
6363
if not candidates: return "No primes found."
6464
max_candidate = max(candidates)
65-
TRIAL_THRESHOLD = 10_000
66-
SIEVE_THRESHOLD = 10_000_000
65+
TRIAL_THRESHOLD = 10_000 # this is the threshold for the trial division (balanced)
66+
SIEVE_THRESHOLD = 10_000_000 # this is the threshold for the sieve (balanced)
6767
primes_found = []
6868
trial_checked = set()
69-
for val in sorted(candidates):
70-
if val <= TRIAL_THRESHOLD:
71-
if is_prime_trial(val):
69+
for val in sorted(candidates): # this will iterate through the candidates
70+
if val <= TRIAL_THRESHOLD: # if the number is less than the threshold, then it will use the trial division
71+
if is_prime_trial(val): # if the number is prime, then it will add it to the list
7272
primes_found.append(val)
7373
trial_checked.add(val)
74-
medium_candidates = sorted(n for n in candidates if TRIAL_THRESHOLD < n <= SIEVE_THRESHOLD)
75-
if medium_candidates:
76-
low, high = min(medium_candidates), max(medium_candidates)
77-
is_prime_segmented, primes_from_sieve = sieve_segment(low, high)
78-
for val in medium_candidates:
79-
if val - low >= 0 and is_prime_segmented[val - low]:
74+
medium_candidates = sorted(n for n in candidates if TRIAL_THRESHOLD < n <= SIEVE_THRESHOLD) # this will get the medium candidates
75+
if medium_candidates: # if there are medium candidates
76+
low, high = min(medium_candidates), max(medium_candidates) # get the min and max of the medium candidates
77+
is_prime_segmented, primes_from_sieve = sieve_segment(low, high) # this will get the prime numbers from a limit
78+
for val in medium_candidates:
79+
if val - low >= 0 and is_prime_segmented[val - low]: # if the number is prime, then it will add it to the list
8080
primes_found.append(val)
81-
for val in sorted(n for n in candidates if n > SIEVE_THRESHOLD):
81+
for val in sorted(n for n in candidates if n > SIEVE_THRESHOLD): # if the number is greater than the threshold, then it will use the hybrid approach
8282
if is_prime_large(val):
83-
primes_found.append(val)
83+
primes_found.append(val) # if the number is prime, then it will add it to the list
8484
if not primes_found:
8585
return "No primes found."
8686
count = len(primes_found)
@@ -90,4 +90,5 @@ def extract_primes(binary_str, N):
9090
return (f"6: {primes_found[0]}, {primes_found[1]}, {primes_found[2]}, ..., "
9191
f"{primes_found[-3]}, {primes_found[-2]}, {primes_found[-1]}")
9292
if __name__ == "__main__":
93-
print(extract_primes(input("Enter binary string: "), int(input("Enter N: "))))
93+
print(extract_primes(input("Enter binary string: "), int(input("Enter N: "))))
94+
# Code made and maintained by Elias Andrew Ailenei (github.com/eliasailenei)

0 commit comments

Comments
 (0)