【问题标题】:2D iterator access in C++C++ 中的 2D 迭代器访问
【发布时间】:2017-08-22 01:14:22
【问题描述】:

我正在使用 std::map() 处理 2D 表,以计算一个数字转换为另一个数字的次数。我遇到了两个问题。首先,我的第一个转换没有显示 (1->2)。其次,我所有的转换都只显示一次(2->3 和 3->1 都发生了两次)。

我明白为什么转换只发生一次。迭代器看不到 currentVal 并转到 else,在其中添加值然后退出。我不知道如何解决这个问题。任何帮助表示赞赏!

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

using namespace std;

//import midi notes
vector <int> midiFile = {1, 2, 3, 1, 20, 5, 2, 3, 1};

//create a 2d hashmap for matrix
map <string, map <string, int> > mCounts;

//strings for the previous value and current value
string prevVal = "";
string currentVal = "";

void addNumbers(vector <int> midiFile) {

    for (int i = 0; i < midiFile.size(); i++) {
        currentVal = to_string(midiFile[i]);

        if(prevVal == "") {
            prevVal = currentVal;   //first value
        } else {
            //playCounts is temporary map to store counts of current val in relation to previous val
            map <string, int> playCounts;

            map <string, int> ::iterator iterator;
            iterator = playCounts.find(currentVal);

            //if mCounts doesn't contain the value yet, create a new hashmap
            if(iterator != playCounts.end()){

                int counter = iterator -> second;
                mCounts[prevVal] [currentVal] = counter + 1;


            } else {
                playCounts.insert(pair <string, int>(currentVal, 1));
                mCounts [prevVal] = playCounts;

            }

            prevVal = currentVal;

        }

        //find values already in map
        map <string, map <string, int> > ::iterator it;
        it = mCounts.find(prevVal);

        if (it != mCounts.end()) {
            //if value is found, do nothing
        } else {
            mCounts.insert(pair <string, map <string, int>>(prevVal, map <string, int>()));
        }
    }
}

【问题讨论】:

  • 尽量避免养成using namespace std的习惯。以后可能会导致很多混乱。
  • 我认为您在循环中创建playCounts 是否正确,即在每次循环迭代时创建一个空地图?
  • 顺便说一句:我建议使用单个地图(不是“2D”地图)并使用 2 .. 3 之类的转换作为 2-&gt;3 之类的单个键并相应地管理计数。
  • 谢谢塔德曼。是的,斯蒂芬,这是一个很好的观点。我创建它时认为每次迭代都会有不同的值,但也许我需要将 playCounts 设为全局变量。
  • 好的,我已将playCounts 设为全局,这已修复了大部分过渡,尽管其中一些不正确。

标签: c++ iterator stdmap


【解决方案1】:

尝试以下方法,其中将组成转换的两个整数组合成"1-&gt;2" 形式的单个字符串,然后用作计数映射中的键。这样代码变得更加简洁。此外,我消除了全局变量并使其成为局部变量或参数:

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

using std::vector;
using std::map;
using std::string;

void addNumbers(const vector <int> &midiFile, map <string, int> &mCounts) {

    for (int i = 0; i < midiFile.size()-1; i++) {

        int prev = midiFile[i];
        int curr = midiFile[i+1];
        std::stringstream ss;
        ss << prev << "->" << curr;

        mCounts[ss.str()]++;
    }
}


int main(int argc, char* argv[])
{
    vector <int> midiFile = {1, 2, 3, 1, 20, 5, 2, 3, 1};
    map <string, int> mCounts;

    addNumbers(midiFile, mCounts);
    for (auto const& x : mCounts)
    {
        std::cout << x.first  // transition
        << ':'
        << x.second // count
        << std::endl ;
    }

    return 0;
}

输出:

1->2:1
1->20:1
2->3:2
20->5:1
3->1:2
5->2:1

【讨论】:

  • 欣赏这一点。我对这种方法唯一关心的是,我将使用这些过渡来定义将要实时播放的 MIDI 音符(马尔可夫链)。因此,当音符 20 播放时,我需要一种方法将该数字引用到其适当的过渡 (20->5) 中,但这些值是链接在一起的。我能看到的唯一方法是添加外部地图,除非您看到另一种方式?
【解决方案2】:

这是不使用嵌套映射且不将注释转换为字符串的解决方案(但假定注释是非负数):

// This snippet uses c++11 syntax
#include <map>

// Code in this example assumes that valid notes are nonnegative
struct transition {
    int from;
    int to;
};

// Comparison operator required to make transition usable as a
// key in std::map
bool operator< (const transition& l, const transition& r) {
    return l.from < r.from || (l.from == r.from && l.to < r.to);
}

// Range of all transitions with respective counter
// starting from a particular note 
std::pair<std::map<transition, int>::const_iterator,
    std::map<transition, int>::const_iterator>
transitions_from(int from_note,
        const std::map<transition, int>& transition_counters) {
    return std::make_pair(transition_counters.lower_bound(transition{from_note, -1}),
            transition_counters.upper_bound(transition{from_note + 1, -1}));
}

int counter_for(transition t, const std::map<transition, int>& transition_counters) {
    const auto it = transition_counters.find(t);
    if (it != transition_counters.end()) {
        return it->second;
    } else {
        return 0;
    }
}

使用示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notes = {1, 2, 3, 1, 20, 5, 2, 3, 1};
    std::map<transition, int> transition_counters;
    int previous_note = -1;
    for (int note: notes) {
        if (previous_note != -1) {
            transition t{previous_note, note};
            transition_counters[t] += 1;
        }
        previous_note = note;
    }

    std::cout << "all encountered transitions:\n";
    for (const auto& entry: transition_counters) {
        std::cout << '(' << entry.first.from << " -> " << entry.first.to << "): " << entry.second << '\n';
    }

    std::cout << "transitions from 1:\n";
    const auto transitions_from_1 = transitions_from(1, transition_counters);
    for (auto it = transitions_from_1.first; it != transitions_from_1.second; ++it) {
        std::cout << '(' << it->first.from << " -> " << it->first.to << "): " << it->second << '\n';
    }

    std::cout << "counters for individual transitions:\n";
    std::cout << "(1 -> 2): " << counter_for(transition{1, 2}, transition_counters) << '\n';
    std::cout << "(2 -> 1): " << counter_for(transition{2, 1}, transition_counters) << '\n';
}

【讨论】:

    【解决方案3】:

    您正在处理小于 128 的小整数。只需使用矩阵,其中transition[i][j] 是从 i 到 j 的转换数。通常,我建议使用带有索引乘法的矩阵的平面缓冲区来访问 2d 维度,或者使用预先编写的类包装器来访问同一事物(请参阅Eigen)。但是在这种情况下,矩阵是如此之小,您可以使用

    int transition[128][128];
    

    当然你想输入这个并通过引用传递它。 不仅您的所有操作会更容易、更透明,而且使用转移矩阵可以进行其他任何方式都无法进行的分析:平衡状态的特征向量等。

    对于转换稀疏且您负担不起密集矩阵的较大问题,请使用实际的稀疏矩阵类,这本质上是您尝试自己滚动的。

    typedef int transitionMatrix[128][128];
    
    void addNumbers(const vector <int> &midiFile, transitionMatrix &mCounts) {
        for (int i = 0; i < midiFile.size()-1; i++) {
            int prev = midiFile[i];
            int curr = midiFile[i+1];
            mCounts[prev][curr]++;
        }
    }
    

    【讨论】:

    • 这很好用。这是我对如何处理它的最初想法,但认为 128x128 矩阵需要计算很多。还有一个问题。 mCounts 结果打印为 midiFile 通过 for 循环处理,但是我希望它仅在 for 循环完成后打印所有结果。对此有什么想法吗?担心当我计算我的随机化权重时,计数可能会扭曲我的结果。谢谢。
    • 你没有显示任何打印代码,我上面的 sn-p 没有打印任何东西。调用addNumbers 后,您将获得一个完成的矩阵,您可以随意打印。您可能需要一个 print 方法,它在两个索引上运行双循环,测试非零,并打印索引和计数。
    猜你喜欢
    • 1970-01-01
    • 2019-12-06
    • 2013-08-14
    • 2020-04-23
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 2011-02-13
    相关资源
    最近更新 更多