ラムダを使用して2Dベクトルの最小要素を見つけようとしています

Dec 10 2020

私は現在、2Dベクトルの最小要素を見つけようとしています。私はC ++ 11ラムダ関数を使用して練習しようとしていますが、これは良い習慣かもしれないと考えましたが、コンパイルできないようです。

私は次のことができることを知っています:

vector<vector<int>> matrix = {
                                {1, 2, 3, 4, 5 },
                                {6, 7, 8, 9, 10 },
                                {5, 6, 8, 1, 12 },
                                {1, 7, 2, 4, 18 },
};

int result = std::numeric_limits<int>::max();
for(const auto& row : matrix)
{
  int minElemInRow = *std::min_element(row.begin(), row.end());
  result = std::min(result , minElemInRow);
}
return result;

しかし、ラムダ関数でも同じことができるかどうか疑問に思っていました。現在、これは私の最善の試みです:

vector<vector<int>> matrix = {
                                {1, 2, 3, 4, 5 },
                                {6, 7, 8, 9, 10 },
                                {5, 6, 8, 1, 12 },
                                {1, 7, 2, 4, 18 },
};

return *std::min_element(matrix.begin(), matrix.end(), 
  [](const auto& row)
  {
    return *std::min_element(row.begin(), row.end());
  });

エラーが発生します:エラーC2672: '演算子__surrogate_func':一致するオーバーロードされた関数が見つかりません

動作するはずだと私が感じるのは、外側のmin_elementが一度に1行ずつ渡され(これは単なるベクトルへの参照です)、そこから最小値を返すことができ、それが他の行と比較されます。

問題は、ラムダがintのベクトルへの参照ではなく、intのベクトルへのイテレーターを受け取ることである可能性があると思いましたが、間接参照は役に立たないようです。

私がやろうとしていることをするためのより良い方法はありますか?

@assembly_wizardは、min_elementが、渡された2つのアイテムを比較できる述語を必要としていることを指摘しました。それは2行です。これにより、次のコードが生成されます。

vector<vector<int>> matrix = {
                                {1, 2, 3, 4, 5 },
                                {6, 7, 8, 9, 10 },
                                {5, 6, 8, 1, 12 },
                                {1, 7, 2, 4, 18 },
};

auto i = std::min_element(matrix.begin(), matrix.end(),
        [](const auto& lhs, const auto& rhs)
{
        return *std::min_element(lhs.begin(), lhs.end()) <
            *std::min_element(rhs.begin(), rhs.end());
});

これにより、要素が最小の行が検索されます。それをさらに別のstd :: min_elementでラップすることでそれを機能させることができますが、それはリモートで役立つよりもはるかに複雑になっています。誰かがより良い提案を持っているなら、私はそれを聞いてみたいです!

回答

2 assembly_wizard Dec 10 2020 at 08:30

コメントで述べたことを実行する作業バージョンをコンパイルしました。

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3, 4, 5 },
        {6, 7, 8, 9, 10 },
        {5, 6, 8, 1, 12 },
        {1, 7, 2, 4, 18 },
    };

    std::vector<int> row_minimums(matrix.size());
    std::transform(matrix.begin(), matrix.end(), row_minimums.begin(), [](const auto& row) {
        return *std::min_element(row.begin(), row.end());
    });
    auto i = *std::min_element(row_minimums.begin(), row_minimums.end());

    std::cout << "Minimum element is: " << i << std::endl;
}

godboltで実際に動作するのを見る

これは各行の最小値を個別に取得row_minimumsするため、intのベクトルであるものを取得し、すべての行の間で最終結果を取得するためにこれらの最小値を取得します。

このコードをforループバージョンよりも悪くしている唯一のことは、row_minimums実行min_elementする前に、すべてを一度にメモリに保持することです。残念ながら、これを同時に行う方法はわかりませんが、私はSTLが期待する最高のものではないため、方法があるかもしれません。

検討する可能性のある他のオプションは、最初に2Dマトリックスを1Dベクトルに連結してから使用min_elementするか、編集に含めたmin_element3回呼び出すオプションです。

また、このSOの回答には、boostライブラリを使用したソリューションに関する興味深い情報が含まれているようですが、それが何であるかは正確にはわかりません。

1 Loreto Dec 10 2020 at 14:52

少し簡単です。std:: for_eachを使用して、行列内の各ベクトルを反復処理し、それらの最小要素を取得します。min参照によって捕獲され、あなたはそれらのすべての分を取得します。

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3, 4, 5 },
        {6, 7, 8, 9, 10 },
        {5, 6, 8, 1, 12 },
        {1, 7, 2, 4, 18 },
    };

    int min = std::numeric_limits<int>::max();

    std::for_each(matrix.begin(), matrix.end(), 
        [&min](const auto& v) 
        { 
           min = std::min(*min_element(v.begin(), v.end()), min);
        }
    );

    std::cout << "Minimum element is: " << min << std::endl;
}