Tuesday, 25 January 2022

Iterative vs Recursive

There are a few programming problems that can be solved using more than one style. For example - Print all combinations of size r in an array of size n. It can be solved using at least 2 approaches - a recursive approach and an iterative approach.

While you can understand the fundamental approach of breaking the problem into repeated problem with a reduced data set, goal here is to compare their runtime durations.

Recursive

         private static void PrintCombinationRecursive(int[] arr, int size) {

            List<List<int>> output = new List<List<int>>();

            Helper(arr, size, 0, arr.Length - 1, new List<int>(), output);

            Console.WriteLine($"PrintCombinationRecursive-count:{output.Count}");

        }

        private static void Helper(int[] arr, int size, int start, int end, List<int> currentList, List<List<int>> finalList) {

            if (start > end)             {

                if (currentList.Count == size)                 {

                    finalList.Add(Clone(currentList));

                }

                return;

            }


            Helper(arr, size, start + 1, end, Clone(currentList), finalList);

            currentList.Add(arr[start]);

            Helper(arr, size, start + 1, end, Clone(currentList), finalList);

        }

        private static List<int> Clone(List<int> items)         {

            var newList = new List<int>();

            foreach (var item in items)             {

                newList.Add(item);

            }

            return newList;

        }

Iterative

         private static void PrintCombinationIterative(int[] arr, int size)         {

            if (arr.Length < size)             {

                return;

            }

            Queue<List<(int, int)>> queue = new Queue<List<(int, int)>>();

            int i = 0;

            int counter = 0;

            foreach (var val in arr)             {

                queue.Enqueue(new List<(int, int)> { (val, i) });

                i++;

            }


            while (queue.Count > 0)             {

                var item = queue.Dequeue();

                if (item.Count < size)                 {

                    var last = item[item.Count - 1];

                    if (last.Item2 >= arr.Length - 1)                     {

                        continue;

                    }

                    var clone = Clone(item);

                    item.Add((arr[last.Item2 + 1], last.Item2 + 1));

                    queue.Enqueue(item);

                    clone[clone.Count - 1] = (clone[clone.Count - 1].Item1, clone[clone.Count - 1].Item2 + 1);

                    queue.Enqueue(clone);

                }

                else                 {

                    counter++;

                }

            }

            Console.WriteLine($"PrintCombinationIterative-count:{counter}");

        }


        private static List<(int, int)> Clone(List<(int, int)> items)         {

            List<(int, int)> newList = new List<(int, int)>();

            foreach (var item in items)             {

                newList.Add(item);

            }

            return newList;

        }

Driver code

            int[] arr = new int[20] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };

            int r = 2;

            for (int i = 0; i < 3; i++)

            {

                Stopwatch sw = Stopwatch.StartNew();

                PrintCombinationRecursive(arr, r);

                sw.Stop();

                Console.WriteLine(sw.ElapsedMilliseconds);

            }


            for (int i = 0; i < 3; i++)

            {

                Stopwatch sw = Stopwatch.StartNew();

                PrintCombinationIterative(arr, r);

                sw.Stop();

                Console.WriteLine(sw.ElapsedMilliseconds);

            }

Output

PrintCombinationRecursive-count:190

268

PrintCombinationRecursive-count:190

235

PrintCombinationRecursive-count:190

217

PrintCombinationIterative-count:190

4

PrintCombinationIterative-count:190

0

PrintCombinationIterative-count:190

0

Conclusion

Allocating stacks for function calls during recursive calls add to the time taken to complete the code logic. Iterative code is more efficient. Point to note is that runtime complexity is about the same.

Monday, 19 April 2021

Branchless programming

Recently came across this wonderful video about branchless programming. It is an interesting concept which, in probability, not required when building applications that are super sensitive about resources usage or the performance. Irrespective of that, it is good to know that there are simple mathematical techniques (which i guess have been around for quite a long time) which allow us to write code that can faster than what we imagine. There is a lot of content available if you search the topic "branchless programming" on your favorite search engine - however, i found this video to be quite detailed and informative.

Sunday, 11 April 2021

Course Schedule (LeetCode problem): C#, Python

Continuing our journey of understanding different languages, we will focus on another LeetCode problem - course schedule

Essentially the problem talks about n courses which have their prerequisites defined in a set of pairs and we need to find out if it is possible to take all courses in single semester. In other words, it is not possible to cover all courses if there is at least one circular dependency amongst the courses.

Few key points:

1. The dependency amongst the courses can be visualized as a directed graph where an edge defines a dependency path from course A to course B.

2. Topological sorting is a standard way of finding cycles in a graph.

A topological ordering is possible if and only if the graph has no directed cycles, that is, if it is a directed acyclic graph (DAG).

Let us look at the code.

C# (implementing using BFS)

public bool CanFinish(int n, int[][] p) {

       

    }

Monday, 11 January 2021

Cross cutting concerns in Clean architecture

Handling cross cutting concerns in application code can become cumbersome if right approach is not utilized. Imagine dealing with caching, logging, authentication, authorization etc. in your core application code wherein those blocks of code bear no core content. Cross cutting concerns are best handled as "aspects" and should be configured/attached as behavior to the host/infrastructure instead of core application code. We tried to explain the same concept in a video. Hope you like it.


Find Kth Largest Number in an Array : Python, C#, Java

Continuing on the journey to understand the strength and weakness of languages when solving algorithm problems, let us look at the leetcode problem of finding kth largest number in an array.

In essence, the most common way to address any algorithm problem that talks about kth largest or kth smallest element in an stream or an array, is to utilize a priority queue that keeps the internal set of elements in sorted order (insert would take log(k) where k is the size of priority queue) and lets you remove the min or max element as O(1) operation. There are different implementations of priority queues available in different languages - most typical one is using a heap (min or max). Here is a quite useful thread that talks about heaps and priority queue.

Back to our problem. Below are possible solutions in C#, Python and Java. I did not attempt JavaScript as it does not have a built in type of priority queue; you could implement it though (reference).

Python (heapq makes it easy)

def findKthLargest(self, nums: List[int], k: int) -> int:

        pq = []

        for i in range(len(nums)):

            heapq.heappush(pq, nums[i])

            if(len(pq) > k):

                heapq.heappop(pq)

        return pq[0]

C# (No in-built data type. Can use SortedList which is close to a minHeap but you need to manage duplicate elements on your own)

public int FindKthLargest(int[] nums, int k) {

        SortedList<int,int> sortedList =new SortedList<int,int>();

        int itemCount=0;

        foreach(int num in nums)

        {

            if(sortedList.ContainsKey(num))

                sortedList[num] += 1;

            else

                sortedList[num] = 1;

            itemCount++;

            if(itemCount > k)

            {

                if(sortedList[sortedList.Keys.First()]==1)

                    sortedList.Remove(sortedList.Keys.First());

                else

                    sortedList[sortedList.Keys.First()]--;

                itemCount--;

            }

        }

        return sortedList.Keys.First();

    }

Java (use in-built PriorityQueue type)

public int findKthLargest(int[] nums, int k) {

        PriorityQueue<Integer> heap = new PriorityQueue<>();

        for(int num: nums){

            heap.offer(num);

            if(heap.size() > k){

                heap.poll();

            }

        }

        return heap.peek();

    }

Observations

1. Since Java and Python have in-built support for priority queue, you can simply focus on logic. Since we are trying to utilize a makeshift type in C#, we have to deal with more than just logic to write the correct code.

2. Test results from leetcode submission. Java consumed highest amount of memory but took the least amount of time. Python produced a balanced result though.