简体   繁体   中英

How to map/ index vectors(of integers) for fast access and comparison c++

I have a vector of vector of int that grows very large (intentional).

vector<vector<int>> coinGroups;

In each step of my algorithm, I attempt to combine each (vector of int) with each other one. For a combine to pass and be added to the vector vector for the next loop iteration, it has to pass three criteria, two of which are super fast and not relevant here, but the one that is slow is my issue:

Head has to match tail:

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

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

*I tried map with the vector of int as the key... but that was super slow (i assume i did it wrong)

Here is a standalone .cpp if anyone needs it or if it makes more sense:

arguments are nothing (defaults to 12 12) or two space separated integers greater than 3 (forewarning this algo 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 there a storage object and or strategy by which I could label all unique combos before entering the n^2 loops so that I could just compare labels instead of memberwise combos? *each member of coinGroups would have a tail and head combo to be labeled/indexed so that I would not have to iterate through all the items in the combo more than once for each member.

You can go with a tree-like structure.

The tree works like this: Every edge is labeled by a coin. The leaves of the tree store references to all members of your vector which have a tail that matches the trace from the root to that leave.

In the pre-processing, you create that tree by going through all vectors and try to traverse the tree, if not possible extend the tree.

In the main routine, you simply try to traverse the tree for every head of a vector.

The resulting complexity for both is O(N * M * deg), with deg being the maximal out-degree of the nodes in that tree, which is smaller than the amount of different coins, N being the amount of vector and M the size of the tail/head. Assuming that M and deg do not rise with bigger inputs, this is O(N).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM