Wednesday 30 December 2015

Find the Nth Multiple Given a Set

1. Problem Description
This is a Google interview question for software development engineer from careercup. Here is the original thread,
"
Find the n-th smallest multiple given a set of numbers. For example, set = {4, 6}, n = 6: 

The sequence is:

4, 6, 8, 12, 16, 18, etc...

Answer is 18
- supatroopa 19 days ago in United States | Report Duplicate | Flag ".

2. Analysis
A similar sort of question can be found here, Find the Nth Expressible Number of Given Set of Prime Numbers.This interview question is relatively simpler than finding the Nth expressible number of a given set of prime number.
For each number in the given set, the following sequence number is quite simple to generate - increment by itself on its previous number (multiple). And the only pitfall is that don't count the common divider by multiple times. This pitfall can be easily avoided as the min-priority-heap will squeeze out the minimal value of multiples of all elements in the given set. Compare the value at the top of min-priority-heap with last value and skip it (don't count this time) if they're equal. Because it is one of common dividers of at least of two elements in the given set. Keep doing this until reach Nth.

3. C++ Implementation
// **********************************************************************************
// Implementation
// **********************************************************************************
#include <queue>
#include <vector>

struct ValueBasePair
{
    ValueBasePair(unsigned long val, unsigned long b)
        : value(val), base(b)
    {}
    unsigned long value;
    unsigned long base;
};

struct ValueBasePairComparator
{
    bool operator()(const ValueBasePair& lhs, const ValueBasePair& rhs) {
        return lhs.value > rhs.value;
    }
};

using MinHeap2 = std::priority_queue<ValueBasePair,
                                     std::vector<ValueBasePair>,
                                     ValueBasePairComparator>;

unsigned long FindNthMultipleNumGivenSet(const unsigned long Nth,
                                         MinHeap2& topNums)
{
    unsigned long numCounter = Nth;
    unsigned long lastVal = 0;
    while (true) {
        const ValueBasePair kvp = topNums.top();
        // pop out a value from the priority queue
        // count until the Nth
        // But only count if this number is different from the last
        // because common mulitple can only count as once
        // For example {4, 6}, numbers like 12, 24 count only once
        if (kvp.value != lastVal) {
            --numCounter;
            lastVal = kvp.value;
        }
        if (numCounter == 0) {
            return kvp.value;
        }

        // pop out the value from the priority queue
        // and push its successor from the value list into the priority queue
        // Successor: <multiple, base> -> <multiple + base, base>
        topNums.pop();
        topNums.push(ValueBasePair(kvp.value + kvp.base, kvp.base));
    }

    return 0;
}

unsigned long FindNthMultipleNumGivenSet(
    const std::vector<unsigned long>& sortedPrimes,
    const unsigned long Nth)
{
    if (sortedPrimes.empty() || Nth == 0) {
        // indicates failure
        return 0;
    }

    if (Nth <= sortedPrimes.size()) {
        return sortedPrimes[Nth - 1];
    }

    MinHeap2 topNums;
    auto iterEnd = sortedPrimes.end();
    // Starting point:
    // the values: each number x 1
    // the priority queue is (multiple, base) - <value, base> pair
    for (auto iter = sortedPrimes.begin(); iter != iterEnd; ++iter) {
        topNums.push(ValueBasePair(*iter, *iter));
    }

    return FindNthMultipleNumGivenSet(sortedPrimes, Nth, topNums);
}
// **********************************************************************************
// Test
// **********************************************************************************
#include <cassert>

void TestFindNthMultipleNum()
{
    std::vector<unsigned long> sortedPrimes;
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 0);

    sortedPrimes.push_back(4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 20);

    sortedPrimes.push_back(6);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 0) == 0);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 1) == 4);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 2) == 6);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 3) == 8);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 4) == 12);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 5) == 16);
    assert(FindNthMultipleNumGivenSet(sortedPrimes, 6) == 18);
}

No comments:

Post a Comment