J'ai un vecteur d'entrée qui peut être de n'importe quelle taille. Ce que je veux, c'est diviser ce vecteur en vecteurs de taille 64 chacun et faire quelque chose. La taille du vecteur d'entrée ne doit pas nécessairement être de taille multiple à 64.

Disons que j'ai un vecteur de taille 200, alors je devrais le diviser en 3 vecteurs de taille 64 et 1 vecteur de taille 8.

Ce à quoi j'ai pensé jusqu'à présent est le suivant:

vector<double> inputVector;
vector<vector<double>> resultVector;

UInt16 length = inputVector.size();
int div = (length % 64) == 0 ? length / 64 : (length / 64) + 1;

for (int i = 0, j = 0; i < div; i++) {
    vector<double> current
    for (int k = 0; k < 64; k++) {
        current.push_back(inputVector[j]);
       if (j++ >= length) break;
    }
    resultVector.push_back(current);
    if (j >= length) break;
}

Je suis sûr qu'il y aurait une meilleure façon de le faire mais je n'ai trouvé aucun exemple

1
Hasan H 17 sept. 2020 à 11:19

5 réponses

Meilleure réponse

Vous pouvez utiliser des itérateurs pour créer un sous-vecteur:

vector<double> inputVector;
vector<vector<double>> resultVector;

for (auto it = inputVector.cbegin(), e = inputVector.cend(); it != inputVector.cend(); it = e) {
    e = it + std::min<std::size_t>(inputVector.cend() - it, 64);
    resultVector.emplace_back(it, e);
}
4
Thomas Sablik 17 sept. 2020 à 08:46

Le plus simple est juste pour chaque élément push_back vers un vecteur, gardez-en une trace, et si la taille de bloc est atteinte, alors "videz-les" sur le vecteur de sortie:

template<typename T>
std::vector<std::vector<T>> devide(const std::vector<T>& v, size_t chunk) {
    // iterative algorithm
    std::vector<T> tmp;
    std::vector<std::vector<T>> ret;
    size_t cnt = 0;
    for (auto&& i : v) {
        tmp.push_back(i);
        ++cnt;
        if (cnt == chunk) {
             cnt = 0;
             ret.push_back(tmp);
             tmp.clear();
        }
    }
    if (cnt != 0) {
        ret.push_back(tmp);
    }
    return ret;
}

Mais cette approche itérative n'est pas optimale - nous pourrions copier des morceaux de mémoire. Donc, parcourez le vecteur et copiez jusqu'à un nombre d'éléments dans chaque boucle - et copiez moins sur la dernière boucle.

template<typename T>
std::vector<std::vector<T>> devide2(const std::vector<T>& v, size_t chunk) {
    // chunk algorithm
    std::vector<std::vector<T>> ret;
    const auto max = v.size();
    for (size_t i = 0; i < max; ) {
        const size_t chunkend = std::min(i + chunk, max);
        ret.emplace_back(v.begin() + i, v.begin() + chunkend);
        i = chunkend;
    }
    return ret;
}

Testé sur godbolt.

0
KamilCuk 17 sept. 2020 à 08:35

Plus dans le style STL:

void even_slice(In b, In e, size_t n, F f)
{
    while(std::distance(b, e) >= n) {
        f(b, b + n);
        b = b + n;
    }
    if (b != e) {
        f(b, e);
    }
}

template<typename In, typename Out>
Out even_slice_to_vetors(In b, In e, size_t n, Out out)
{
    using ValueType = typename std::iterator_traits<In>::value_type;
    using ItemResult = std::vector<ValueType>;
    even_slice(b, e, n, [&out](auto x, auto y) { *out++ = ItemResult{x, y}; });
    return out;
}

https://godbolt.org/z/zn9Ex1

0
Marek R 17 sept. 2020 à 09:01

Notez que vous savez exactement combien de sous-vecteurs ont la taille maximale souhaitée:

template<typename It>
auto subdivide_in_chunks(It first, It last, size_t chunk_size) {
    using value_type = typename std::iterator_traits<It>::value_type;
    size_t size{ std::distance(first, last) / chunk_size };
    std::vector<std::vector<value_type>> ret;
    ret.reserve(size);
    auto last_chunk = std::next(first, size * chunk_size);
    while ( first != last_chunk ) {
        auto next = std::next(first, chunk_size);    
        ret.emplace_back(first, next);
        first = next;
    }
    ret.emplace_back(first, last);  // This is the last, shorter one. 
    return ret;
}
0
Bob__ 17 sept. 2020 à 09:25

Avec range-v3, vous pouvez simplement écrire:

namespace rs = ranges;
namespace rv = ranges::views;

auto resultVector = inputVector 
                  | rv::chunk(64) 
                  | rs::to<std::vector<std::vector<double>>>;

Voici une démo.

0
cigien 17 sept. 2020 à 12:48