vendredi 22 octobre 2021

How to avoid excessive for loops in C++

I have been trying to write a small test program and I was trying to think of any potential optimization I could do, especially if there is any chance to avoid some of the for loops.

The program has Dad class, a Child class and a DadChild class that holds the IDs of the Dad and Child objects that are associated based on their DNA.

The requirements are:

  1. A Dad object can be associated with multiple Child objects
  2. A Child object can NOT be associated with more than 1 Dad objects
  3. Out of all Child objects that are associated with the a Dad object, only one has to be marked as bestMatch.

Here is the sample code that I drafted, it works as expected, but I wondering if there is any space for optimizations.

#include<iostream>
#include<vector>
#include<map>
#include <algorithm>
#include <memory>


class Dad
{
public:
    Dad(int a_id, int a_dna): id(a_id), DNA(a_dna) {}

    int id;
    int DNA;
};

class Child
{
public:
    Child(int a_id, int a_dna): id(a_id), DNA(a_dna) {}

    int id;
    int DNA;
};

class DadChild
{
public:
    DadChild(): dnaResult(-1) {}
    DadChild(bool a_bestMatch, int a_childID, int a_dadID, double a_dnaResult):
        bestMatch(a_bestMatch),
        childID(a_childID),
        dadID(a_dadID),
        dnaResult(a_dnaResult) {}

    int dadID;
    int childID;
    double dnaResult;
    bool bestMatch;
};

double compareDNA (int a, int b) {
    return abs(a - b); // 0 indicates a perfect match
}

DadChild createDadChild(Dad d) {
    //Do something for single parents. Not really that important here.
    return DadChild();
}

DadChild createDadChild(Dad d, Child c, double dnaTest) {
    //Construct and return DadChild Object
    DadChild dc;
    dc.bestMatch = false;
    dc.childID = c.id;
    dc.dadID = d.id;
    dc.dnaResult = dnaTest;

    return dc;
}

int main() {

    Dad d1 (1, 1);
    Dad d2 (2, 4);

    Child c0 (0, 4);
    Child c1 (1, 2);
    Child c2 (2, 3);
    Child c3 (3, 1);

    std::vector<Dad> dads;
    std::vector<Child> children;


    dads.push_back(d1);
    dads.push_back(d2);

    children.push_back(c0);
    children.push_back(c1);
    children.push_back(c2);
    children.push_back(c3);

    std::map <int, DadChild> assocMap; // Where the the key is the childID
    std::vector <DadChild> singleDadVector;

    for (auto &dad : dads) {
        bool singleParent = true;
        for (auto &child : children) {
            double dnaTest = compareDNA(dad.DNA, child.DNA);
            if (dnaTest < 2) { // 2 here is an arbitrary threshold for the dna result
                singleParent = false;
                auto search = assocMap.find(child.id);
                if (search != assocMap.end()) {
                    if (assocMap[child.id].dnaResult > dnaTest) {
                        assocMap[child.id] = createDadChild(dad, child, dnaTest);
                    }
                }
                else {
                    assocMap[child.id] = createDadChild(dad, child, dnaTest);
                }
            }
        }
        if (singleParent)
            singleDadVector.push_back(createDadChild(dad));
    }


    // Try to find the bestMatch.
    // I am wondering if there is a better way to do this.
    for (auto const &dad : dads) {
        DadChild dc;
        DadChild *bestDadChild = &dc;
        for (auto &assoc: assocMap) {
            if ((dad.id == assoc.second.dadID) && (bestDadChild->dnaResult == -1)) {
                bestDadChild = &assoc.second;
            } else if ((dad.id == assoc.second.dadID) && assoc.second.dnaResult < bestDadChild->dnaResult) {
                bestDadChild = &assoc.second;
            }
        }
        bestDadChild->bestMatch = true;
    }

    /*
    // I tried to do something like this, but it didn't really work.
    for (auto &dad : dads) {
        auto pr = std::min_element(
            std::begin(assocMap), std::end(assocMap),
            [dad] (const auto &p1, const auto &p2) {
                return ((dad.id == p1.second.dadID) && (dad.id == p2.second.dadID) && (p1.second.dnaResult < p2.second.dnaResult));
            });
        pr->second.bestMatch = true;
        std::cout << dad.id << std::endl;
        std::cout << "Setting: " << pr->second.dadID << std::endl;
    }*/

    for (auto &a : assocMap) {
        std::cout << "DadID: " << a.second.dadID << std::endl;
        std::cout << "ChildID: " << a.second.childID << std::endl;
        std::cout << "bestMatch: " << a.second.bestMatch << std::endl;
        std::cout << "dnaResult: " << a.second.dnaResult << std::endl;
        std::cout << "--------------" << std::endl;
    }

}

Output:

DadID: 2
ChildID: 0
bestMatch: 1
dnaResult: 0
--------------
DadID: 1
ChildID: 1
bestMatch: 0
dnaResult: 1
--------------
DadID: 2
ChildID: 2
bestMatch: 0
dnaResult: 1
--------------
DadID: 1
ChildID: 3
bestMatch: 1
dnaResult: 0
--------------

Aucun commentaire:

Enregistrer un commentaire