How to match / index vectors (integers) for fast access and comparison C ++

I have an int vector which grows very large (intentional).

vector<vector<int>> coinGroups;

      

At every step of my algorithm, I try to concatenate each (int vector) with each other. For the harvester to be passed and added to the vector vector for the next iteration of the loop, it must pass three criteria, two of which are very fast and not relevant here, but slow is my problem:

The head must match the tail:

//bool match = equal(coinGroups.at(i).begin()+1, coinGroups.at(i).end(), coinGroups.at(ii).begin());

      

Visually this compares (a, b, c, d) with (e, f, g, h) as b == e && c == f && d == g

* I tried a map with an int vector as a key ... but it was very slow (I guess I did it wrong)

Here is a separate .cpp in case anyone needs it or if it makes sense:

arguments are nothing (default 12 12) or two space-separated integers greater than 3 (warning of this algorithm is O (n!) complexity)

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <map>
#include <sstream>

using namespace std;

//min clock size 3
vector<vector<int>> placeCoins(int _clockSize, vector<int> _coins)
{
    int totalCheckedCombos = 0;
    vector<vector<int>> coinGroups;
    vector<int> coinSet = _coins;
    sort(coinSet.begin(), coinSet.end());
    coinSet.erase(unique(coinSet.begin(), coinSet.end()), coinSet.end());

    map<int, int> coinCounts;
    for (int i = 0; i < coinSet.size(); i++)
    {
        coinCounts[coinSet.at(i)] = count(_coins.begin(), _coins.end(), coinSet.at(i));
    }

    cout << "pairs" << endl;
    //generate fair pairs of coins
    for (int i = 0; i < coinSet.size(); i++)
    {
        for (int ii = 0; ii < coinSet.size(); ii++)
        {
            if ((coinSet.at(i) + coinSet.at(ii)) % _clockSize != 0)
            {
                if (i == ii)
                {
                    if (coinCounts[coinSet.at(i)] > 1)
                    {
                        coinGroups.push_back({ coinSet.at(i),coinSet.at(ii) });
                    }
                }
                else
                {
                    coinGroups.push_back({ coinSet.at(i),coinSet.at(ii) });
                }
            }
        }
    }

    cout << "combine" << endl;
    //iteratively combine groups of coins
    for (int comboSize = 3; comboSize < _clockSize; comboSize++)
    {
        totalCheckedCombos += coinGroups.size();
        vector<vector<int>> nextSizeCombos;
        for (int i = 0; i < coinGroups.size(); i++)
        {
            for (int ii = 0; ii < coinGroups.size(); ii++)
            {
                //check combo to match

                //cleaner but slower due to inability to breakout early on compare check
                //bool match = equal(coinGroups.at(i).begin()+1, coinGroups.at(i).end(), coinGroups.at(ii).begin());

                bool match = true;
                for (int a = 0; a < comboSize - 2; a++)
                {
                    if (coinGroups.at(i).at(a+1) != coinGroups.at(ii).at(a))
                    {
                        match = false;
                        break;
                    }
                }

                //check sum
                if (match)
                {
                    vector<int> tempCombo = coinGroups.at(i);
                    int newVal = coinGroups.at(ii).at(coinGroups.at(ii).size()-1);
                    tempCombo.push_back(newVal);
                    if (coinCounts[newVal] >= count(tempCombo.begin(), tempCombo.end(), newVal))
                    {
                        if (accumulate(tempCombo.begin(), tempCombo.end(), 0) % _clockSize != 0)
                        {
                            nextSizeCombos.push_back(tempCombo);
                        }
                    }
                }
            }
        }

        if (nextSizeCombos.size() == 0)
        {
            //finished, no next size combos found
            break;
        }
        else
        {
            cout << nextSizeCombos.size() << endl;
            coinGroups = nextSizeCombos;
        }
    }
    cout << "total combos checked: " << totalCheckedCombos << endl;
    return coinGroups;
}

//arguments are _clockSize _coinCount
//The goal of this algorithm is to create combos of stepSizeTokens (coins) which fill a loop of register locations (clock)
int main(int argc, char *argv[]) {
    int clockSize = 12;
    int coinCount = 12;

    if (argc >= 2)
    {
        std::istringstream iss(argv[1]);
        if (!(iss >> clockSize))
        {
            cout << "argument 1 invalid" << endl;
            cout << "press enter to end program" << endl;
            cin.get();
            return 0;
        }
        std::istringstream iss2(argv[2]);
        if (!(iss2 >> coinCount))
        {
            cout << "argument 2 invalid" << endl;
            cout << "press enter to end program" << endl;
            cin.get();
            return 0;
        }
    }

    if (clockSize < 3) { clockSize = 3; }

    vector<int> coins = {};
    cout << "coin list:" << endl;
    for (int i = 0; i < coinCount; i++)
    {
        int tempCoin = rand() % (clockSize - 1) + 1;
        cout << tempCoin << " , ";
        coins.push_back(tempCoin);
    }
    cout << endl;


    vector<vector<int>> resultOrders = placeCoins(clockSize, coins);

    cout << "max combo size: " << resultOrders.at(0).size() << endl;
    cout << "number of max combos found: " << resultOrders.size() << endl;

    cout << "press enter to end program" << endl;
    cin.get();
}

      

My question is : Is there a storage object and / or a strategy with which I can tag all unique combos before introducing n ^ 2 loops so that I can just compare tags instead of member commits? * each member of coinGroups will have a tail and head combo to be tagged / indexed so that I wouldn't have to iterate over all the items in the combo more than once for each member.

+3


source to share


1 answer


You can go with a tree structure.

The tree works like this: each edge is marked with a coin. The leaves of the tree store references to all members of your vector that have a tail that matches the path from the root to that.

In preprocessing, you create this tree by going through all the vectors and try to traverse the tree if it is not possible to expand the tree.



In the main routine, you just try to traverse the tree for each head of the vector.

The resulting complexity for both is O (N * M * deg), where deg is the maximum degree of the degree of nodes in this tree, which is less than the number of different coins, N is the number of vectors, and M is the tail / head size. Assuming M and deg do not grow with large inputs, this is O (N).

0


source







All Articles