def is_prime_big_hint(): message = """ to check primality of very large numbers (order 10^10) ------Miller Rabin test-------- checks if a number n is prime k is a input parameter which measures accuracy of the test generally k = 4 compute d & r such that d*(2^r) = n-1 ------test logic------ randomly choose 'a' in range(2,n-1) compute x = (a^d)%n if x in [1, n-1]: return true else: keep squaring (with mod n) x while d!=n-1 d*=2 if (x^2)%n == 1: return False else if (x^2)%n ==n-1 :return True the test logic runs k times to ensure accurate results. overall time complexity is O(k.(Log(n))^3) """ print_msg_box(message)
def hint(): message = """ it is used to find primes in range 2 to N sieve of eratosthenes algorithm generates an array in which the value at each index i is a boolean value which tells whether i is a prime number or not. we take a boolean array of size N with default True except for index 0 and 1 the idea is to mark multiples of all primes from 2 to sqrt(N) as False. pseudo code: sieve_array = [ True, True, ..... ] sieve_array[0] = sieve_array[1] = False loop for i from 2 to sqrt(N): if sieve_array[i] is True (means i is Prime): loop for j from i*i to N with step size of i: sieve_array[j] = False Now, if any index i in sieve_array is true then it means i is a prime. the time complexity is O(N.log(log(N))) needs O(N) extra space """ print_msg_box(message)
def modular_inverse_hint(): message = """ the modular inverse for A under mod M is defined as X such that (A*X)%M=1 According to Fermats’s little theorem, X = (A^(M-2))%M , when A and M are co-prime """ print_msg_box(message)
def hint(self): message = """ ______________TRAVELLING SALESMAN PROBLEM______________ given a complete graph, the task is to find the Hamiltonian cycle (path which visits all nodes exactly once and comes back to source) with the minimum weight. solution -------- The possible way to find a solution is to try all Hamiltonian cycles and find one with minimum total weight. but we can improve. we can use dynamic programming to calculate the minimum cost for all possible sub-paths and then combine those results to get our ans. we define our dp state as the minimum cost when ending node is E and we have visited VISITED_SET of nodes. starting at our source, we calculate the answers for all subpaths with our base case as all paths of length 1(or two nodes). since there can be 2^n sub paths ending at any node, the extra space required is of order O(N.2^N). A> lets understand how we will calculate the answer for all subpaths. i> we have answer to all subpaths of length 1. ii> starting from 2, we generate all possible subpaths of length 2 to N which also includes the start node. i> we remove any node (except SOURCE) say NEXT from this set. ii> form the remaining nodes we will select any node except SOURCE, say END, this end will be the end node for all subpaths of length 1 less than current subpath length. i> we then attach our NEXT to this subpath. ii> if this end is giving reduced cost with END ,we keep it. B> finding the minimum cost. we iterate through all the costs of subpaths containing all nodes, the subpath which gives minimum cost value on connecting END to SOURCE is the answer. C> finding the actual path. i> we initialise LAST as SOURCE and VISITED containing all nodes. do untill we get all nodes: i> we find a node with min cost to connect to LAST. ii> set LAST to this node and remove it from VISITED. in this way we keep reducing the size of tour getting an optimal node in each step. The overall time complexity is O(N^2 * 2^N) and space complexity is O(N* 2^N). USAGE ----- tsp_obj = tsp(adjacency_matrix, source_node) # node numbering is expected to start from 0. # a complete graph is expected. # adjacency_matrix must be (N*N) where N is the number of nodes. minimum_cost = tsp_obj.minimum_cost # returns an integer, the minimum_cost of tour. optimal_tour = tsp_obj.optimal_tour # returns a list of integers of size N+1, the optimal path. """ print_msg_box(message)
def is_prime_hint(): message = """ conditions for any number X to be prime: 1. X >= 2 2. 2 is the only even prime 3. X must not be divisible by any number in [2, X) we can optimise this check as follows: i. we need to check for only odd divisors. ii. divisors and corresponding quotients are only possible divisors left and they are unique upto sqrt(X). so we need to check for only odd divisors upto sqrt(X) i.e., from 3 to sqrt(X). """ print_msg_box(message)
def hint(self): message = """ finding prime factorisation of N ----simple factorisation---- 1. while N is even keep dividing N by 2 and keep adding 2 to the answer list. 2. Now that N is odd, do the following: i. run loop for i from 3 to sqrt(N) with step size 2 (taking all odd numbers). i. while i divides N, add i to asnwer list and divide N by i. 3. if N is still not 1 then its a prime, add it to the answer list. The time complexity is O(sqrt(N).log(N)) ----factorisation using sieve---- simple method is very slow if we have to process multiple factorisation queries. the idea is to store the smallest prime divisor of every number then use this value to find the factorisation using recursive division. 1. creating the smallest prime divisor array: pseudo code: divisor_array = [ 0, 1, 2, 3, 4, 5, ....., N ] loop for i from 4 to N with step size of 2: divisor_array[i] = 2 loop for i from 3 to sqrt(N): if divisor_array[i] is i (means i is Prime): loop for j from i*i to N with step size of i: if divisor_array[j] == j: divisor_array[j] = i the time complexity is O(N.log(log(N))) needs O(N) extra space 2. finding the factorisation of X pseudo code: factors = [] while X != 1: factors.append(divisor_array[X]) X = X / divisor_array[X] the time complexity is O(log(N)) """ print_msg_box(message)
def hint(self): message = """ ------formal definition------ number of combinations is defined as: The number of selections of a given number of elements from a larger number without regard to their arrangement. for r elements to be selected from n elements, number of combinations is denoted by nCr nCr = (n!)/((r!)*(n-r)!) ------recursive definition------ C(n, r) = C(n-1, r-1) + C(n-1, r) base case: C(n, 0) = C(n, n) = 1 ------usage------ obj = combinationsDict() print("3C2 =",obj[3,2]) """ print_msg_box(message)
def print_binary_exponentiaition_hint(): message = """ The expression X^Y can be evaluated in log(Y) steps. let ANS = X^Y if Y is even, we can write : ANS = (X^2)^(Y ÷ 2) that is, we square the base and halve the exponent. else if Y is odd : ANS = ANS*X that is, we multiply our ANS once with base so that Y reduces by 1 and becomes even. These two steps are executed repeatedly till exponent (Y) is greater than 0. We can extend this algorithm for modular exponentiation by simply appending a mod operation to the multiplications. The time complexity for this algorithm is O(Log(Y)). """ print_msg_box(message)