Showing posts with label Algorithm. Show all posts
Showing posts with label Algorithm. Show all posts

Thursday, March 24, 2011

masterpiece of bitwise operation...



What does this piece of code do?

It finds one single unique number in an array where all other numbers appear three times. This is essentially mod3.

Explanation (Thanks to Rama B@careercup)

"The code works in similar line with the question of "finding the element which appears once in an array - containing other elements each appearing twice". Solution is to XOR all the elements and you get the answer.

Basically, it makes use of the fact that x^x = 0. So all paired elements get XOR'd and vanish leaving the lonely element.
Since XOR operation is associative, commutative.. it does not matter in what fashion elements appear in array, we still get the answer.

Now, in the current question - if we apply the above idea, it will not work because - we got to have every unique element appearing even number of times. So instead of getting the answer, we will end up getting XOR of all unique elements which is not what we want.

To rectify this mistake, the code makes use of 2 variables.
ones - At any point of time, this variable holds XOR of all the elements which have
appeared "only" once.
twos - At any point of time, this variable holds XOR of all the elements which have
appeared "only" twice.

So if at any point time,
1. A new number appears - It gets XOR'd to the variable "ones".
2. A number gets repeated(appears twice) - It is removed from "ones" and XOR'd to the
variable "twice".
3. A number appears for the third time - It gets removed from both "ones" and "twice".

The final answer we want is the value present in "ones" - coz, it holds the unique element.

So if we explain how steps 1 to 3 happens in the code, we are done.
Before explaining above 3 steps, lets look at last three lines of the code,

not_threes = ~(ones & twos)
ones & = not_threes
twos & = not_threes

All it does is, common 1's between "ones" and "twos" are converted to zero.

For simplicity, in all the below explanations - consider we have got only 4 elements in the array (one unique element and 3 repeated elements - in any order).

Explanation for step 1
------------------------
Lets say a new element(x) appears.
CURRENT SITUATION - Both variables - "ones" and "twos" has not recorded "x".

Observe the statement "twos| = ones & x".
Since bit representation of "x" is not present in "ones", AND condition yields nothing. So "twos" does not get bit representation of "x".
But, in next step "ones ^= x" - "ones" ends up adding bits of "x". Thus new element gets recorded in "ones" but not in "twos".

The last 3 lines of code as explained already, converts common 1's b/w "ones" and "twos" to zeros.
Since as of now, only "ones" has "x" and not "twos" - last 3 lines does nothing.

Explanation for step 2.
------------------------
Lets say an element(x) appears twice.
CURRENT SITUATION - "ones" has recorded "x" but not "twos".

Now due to the statement, "twos| = ones & x" - "twos" ends up getting bits of x.
But due to the statement, "ones ^ = x" - "ones" removes "x" from its binary representation.

Again, last 3 lines of code does nothing.
So ultimately, "twos" ends up getting bits of "x" and "ones" ends up losing bits of "x".

Explanation for step 3.
-------------------------
Lets say an element(x) appears for the third time.
CURRENT SITUATION - "ones" does not have bit representation of "x" but "twos" has.

Though "ones & x" does not yield nothing .. "twos" by itself has bit representation of "x". So after this statement, "two" has bit representation of "x".
Due to "ones^=x", after this step, "one" also ends up getting bit representation of "x".

Now last 3 lines of code removes common 1's of "ones" and "twos" - which is the bit representation of "x".
Thus both "ones" and "twos" ends up losing bit representation of "x".

1st example
------------
2, 2, 2, 4

After first iteration,
ones = 2, twos = 0
After second iteration,
ones = 0, twos = 2
After third iteration,
ones = 0, twos = 0
After fourth iteration,
ones = 4, twos = 0

2nd example
------------
4, 2, 2, 2

After first iteration,
ones = 4, twos = 0
After second iteration,
ones = 6, twos = 0
After third iteration,
ones = 4, twos = 2
After fourth iteration,
ones = 4, twos = 0

Explanation becomes much more complicated when there are more elements in the array in mixed up fashion. But again due to associativity of XOR operation - We actually end up getting answer."


Wednesday, March 23, 2011

Selecting the largest/smallest N numbers out of M numbers

Given sufficient amount of memory to hold all M.

  1. Your gut feeling that this can be a heap sort. O(n*log(n)).
    Don't be shy, say it out load, it is indeed correct and efficient. But also, do not settle.

  2. Your can also do this by using quick sort, by adjusting the next pivot accordingly after partition. Still O(n*log(n)), but in real world the gain here is actually quite significant as you can ignore at least one of the two partitions each time.

  3. In #2, you may have noticed the fact that you are not required to sort the N number. But taking that one step further. You can apply Selection Sort, which is O(n), to find the Nth number during one iteration, and you should have guessed what steps to follow when you have the Nth number at hand.
Even if you want to have the N number sorted, #3 is still the optimal solution as you can take the N number and sort them in O(Nlog(N)) time, which is smaller than O(Mlog(M)) as N.

Update: It seems that I was wrong about #2. #2 is actually linear. See here. Randomized-Select is the name, and it is introduced in Chapter 9 of "Introduction of Algorithms" 2nd Edition.

Read the book, as always...

Tuesday, August 31, 2010

Arrange number 1..n so that each pair of consecutive numbers sum up to a square number

Problem Statement
Write a program to arrange numbers from 1 to n such that the sum of two consecutive numbers is a perfect square.

An interesting way of looking at this problem
Make a graph so that each node is one of the numbers and there is an edge between two nodes if these two numbers can add up to a square number. Find a path starting from one node and end up in another so that each node is visited once and all nodes are visited. Such path may not exist.

There should be an easier way to do this by seeking for nodes with only one edge in/out...

Wednesday, August 25, 2010

Given a dictionary of words and a string with all spaces removed, determine whether the string is composed of valid words

Problem Statement

Given a dictionary of words and a string with all spaces removed, determine whether the string is composed of valid words

Examples
helloworld-> hello world (valid)
isitniceinhere-> is it nice in here (valid)
zxyy-> invalid

Time Complexity Requirement
O(n^2)

Algorithm
True/False array T of size equal to the input. T[i] = true => the substring starting from i is composed of valid words.

Assume API method IsValidWord() is O(1) since we are given a dictionary.

for each element in the array:

T[i] = (IsValidWord(s.substring(i, 1)) && T[i+1])
|| (IsValidWord(s.substring(i, 2)) && T[i+2])
...
|| (IsValidWord(s.substring(i, n-i-1)) && T[n-1])

The result is T[0].

Complexity analysis
for each i, it takes O(n) to compute. and we start the computation from i = s.length - 1 and decrease i, and there are n element in the array to compute. So we get O(n^2) as the total time complexity.

Tuesday, August 24, 2010

Given two strings of a and b. Find the max window in a and b where the substrings in the window are anagrams.

Problem description:

1. Given two strings a and b, find the max value of "length" where for some i and j, a.substring(i, i+length) and b.substring(j, j+length) are anagrams.

2. Given two strings a and b, find the max window of matching patterns between a and b where the pattern matching doesn't have to be an exact match but also can be a permutation of each other. (which is essentially anagrams...)

Algorithm:

let m = a.length;
let n = b.length;
let matrix = int[m][n]

if a[i] == b[j] then matrix[i][j] = 1
else matrix[i][j] = 0

With the matrix populated by the routines above, find the largest square that meets the following two conditions:
  • no column contains all 0s. For each column, at least one cell is 1
  • no rows contains all 0s
The time complexity is O(n^2). I don't believe it can be done in O(nlogn). But I could be wrong.

Someone was asked the following question during interview: if m = n, would that help speeding up the complexity?

Well, I have no idea...

Thursday, July 8, 2010

Checking whether a binary tree is balanced or not using recursion

Balanced: no path from root to leaf node is longer than another such path by 1.

The code is just so elegant...



this can be extended to general trees with more than two children by examining all children's maxDepth or minDepth.

Tuesday, July 6, 2010

Finding all numbers which are smaller than K and only divisible by 2, 3, and 5

This may not be the most optimal solution...

Essentially we are looking for all possible {(x, y, z) | (2^x * 3^y * 5^z) <= K}
  1. When x+y+z = 1, we get {2, 3, 5},

  2. Let:

    S(i) = { (2^x * 3^y * 5^z) | (2^x * 3^y * 5^z) < K and (x + y + z = i)}

    S(i, j) = Union of all s in S(i-1, t) * j that:
    a. t and j belong to {2, 3, 5}
    b. t >= j
    c. s <= K

    for example:
    S(2,2) = {2, 3, 5} * 2 = {4, 6, 10}
    S(2,3) = {3, 5} * 3 = {9, 15}
    S(2,5) = {5} * 5 = {15}

  3. S(i) = S(i-1, 2) + S(i -1, 3) + S(i-1, 5).

    for example:
    S(2) = S(2,2) + S(2,3) + S(2, 5)

  4. Stops when S(i) is empty.

Sunday, September 9, 2007

Online selection algorithm. Random Selection in Linked Lists with Unknown Length

Question: Given a finite linked list with unknown length(in terms of nodes count), select one item(multiple nodes) with equal possibility for every node in the linked list.

* One item only.
Assuming the input list has k nodes. The possibility for each node to be selected is 1/k. We need to guarantee that after we visiting m nodes, the possibility for each node is 1/m.



Proof of correctness:
When i = 0, node 0 is selected with 100% possibility.
When i = 1, node 1 has 50% chance to replace node 0 as selection result. Both of node 1 and node 0 will have equal chance to be chosen.
When i = n(which means we are testing the (n+1)th node), before line 3 is executed, all nodes from node 0 to node n-1 have possibility of 1/n to be selected. The (n+1)th node has 1/(n+1) possibility to be selected at line 3. For the previous selected node, it has a possibility of n/(n+1) to survive this round. Multiplying with its chance to be selected: 1/n, we get 1/(n+1) as well,

*Extended: Multiple items.
Assuming we have k nodes in the list and we are targeting a result set of size m,
each node will have possibility of m/k to be added to the result set.