Finding whether there exists a pair of numbers in a set having a given product
The algorithm description is good, but the implementation of find is \$ O(n) \$, so the overall runtime is actually \$ O(n^2) \$.

Detailed review follows:


There are no docstrings. What does each function do? How do I call it? What does it return? Are there any constraints on the parameters, for example do the inputs have to be sorted? or do they have to be free of duplicates?
It's not necessary in Python to have parentheses around the condition of if statement, and it's best to omit them.
According to the analysis, find needs to be \$ O(\log n) \$, but the actual implementation is \$ O(n) \$. Here's a demonstration:

&gt;&gt;&gt; from timeit import timeit
&gt;&gt;&gt; for p in range(2, 8):
...     items = list(range(10**p))
...     '{:f}'.format(timeit(lambda:find(0, items), number=1))
...
0.000012
0.000022
0.000077
0.001386
0.038858
0.417670


The reason for this is the copying in the recursive calls. In Python, the list slice items[mid:] has to be copied, and the time taken to do this is proportional to the number of items in the copy. See the time complexity page on the Python wiki.
In order to avoid copying in find, it's best to keep indexes into the part of the sequence which might contain the item we're looking for:

def find(x, items):
    """Return True if x is an element of the sequence items, assuming
    items is sorted.

    """
    lo = 0
    hi = len(items)
    while lo &lt; hi:
        mid = (lo + hi) // 2
        if x == items[mid]: return True
        elif x &lt; items[mid]: hi = mid
        else: lo = mid + 1
    return False

Python has a built-in module bisect for binary search of sorted sequences, and using this module it's possible to write find like this:

from bisect import bisect_left

def find(x, items):
    """Return True if x is an element of the sequence items, assuming
    items is sorted.

    """
    i = bisect_left(items, x)
    return i &lt; len(items) and items[i] == x

The corrected version of find locates an element in a sorted list in time \$ O(\log n) \$. But it's possible to do better than that using a set. Set lookup has average complexity \$ O(1) \$.
The implementation of merge goes into an infinite loop if list1 and list2 have a common item. This was easy for me to spot because there is an if … elif with no else at the end. When code has a sequence of conditions like this, it's always worth asking, "what happens if none of the conditions apply?"
Python has a built-in function heapq.merge for merging sorted iterables into a single sorted iterator, and a built-in function sorted for sorting an iterable (using a variant of mergesort, as it happens). These are fast and well-tested implementations, and (except as an exercise) they should be used in preference to writing your own.
In findIfProdExists the division x/item is computed twice in some cases.
It would be better to compute it once and remember the value.
In findIfProdExists the cheap test item!=x/item is computed after the expensive test find(x/item,items). It's usually best to compute cheap cases first so that if they fail the expensive test can be avoided altogether.


Putting all this together, I'd implement the algorithm like this:

def product_pair(x, items):
    """Return True if there is a pair of distinct elements in the iterable
    items whose product is x. Return False otherwise.

    """
    items = set(items)
    for item in items:
        if item != 0 and x % item == 0:
            y = x / item
            if y != item and y in items:
                return True
    return False