【问题标题】:Simultaneous Union and Intersection between two maps in C++C ++中两个映射之间的同时联合和交集
【发布时间】:2020-08-21 09:07:12
【问题描述】:

在做大学项目时,我遇到了以下问题: 我有两个地图(Kmer1 和 Kmer2),它们由一个字符串(键)和一个整数(值)组成。 我必须计算遵循这个公式的距离

[1-(I/U)]*100

Where...
     ...U = the sum of all int values inside Kmer1 U Kmer2
     ...I = the sum of all int values inside Kmer1 ∩ Kmer2

Consider that...
             ... The U and ∩ are made evaluating the keys (strings)
             ... When an element is in both maps:
                 - At the Union we add the one with higher int value
                 - At the Intersection we add the one with lower int value

例子:

Kmer1 = AAB¹ AAC¹ AAG³
Kmer2 = AAG¹ AAT² ABB¹

Union = AAB¹ AAC¹ AAG³ AAT² ABB¹   U= 8
Intersection = AAG¹                I= 1
Distance = 87.5

代码时间! 我一直在尝试解决它,但所有解决方案都像.. 部分正确, 并非所有情况都包括在内。所以当我试图覆盖它们时,我以无限循环结束,异常上升,if-else 的长长的嵌套(这很糟糕......)无论如何,这是最不糟糕且不起作用的尝试:

设置:

Species::Kmer Kmer1, Kmer2;        //The two following lines get the Kmer from another
Kmer1 = esp1->second.query_kmer(); //object.
Kmer2 = esp2->second.query_kmer(); 

Species::Kmer::const_iterator it1, it2, last1, last2;
it1 = Kmer1.cbegin();           //Both Kmer are maps, therefore they are ordered and
it2 = Kmer2.cbegin();           //whitout duplicates.
last1 = --Kmer1.cend();
last2 = --Kmer2.cend();

double U, I;
U = I = 0;

应用公式的循环:

while (it1 != Kmer1.cend() and it2 != Kmer2.cend()){
    if (it1->first == it2->first) {         
        if (it1->second > it2->second) {
            U += it1->second;
            I += it2->second;
        } else {
            U += it2->second;
            I += it1->second;
        }
        ++it1;
        ++it2;

    } else if (it1->first < it2->first) {
        U += it1->second;
        ++it1;
    } else {
        U += it2->second;
        ++it2;
    }
}

请注意,我不是先创建并集和交集,然后再计算它们的总和,而是直接跳到值的总和。我知道也许这并不难,但我一直在尝试解决它,但我几乎陷入困境......


I've uploaded the whole code at Github: (Maybe it helps)
    - There is a makefile to build the code
    - There is a file called input.txt with a sample for this specific problem
    - Also inside the input.txt, after line13 (fin) I've added the expected output
    - Executing ./program.exe < input.txt should be enough to test it.

https://github.com/PauGalopa/Cpp-Micro-Projects/tree/master/Release


重要 是的!我知道几乎所有的 STL 功能都可以在几行中做到这一点,但是...... 由于这是一个大学项目,我受到教学大纲的限制,因此请考虑我只允许使用“地图”“字符串”“向量”等等。 不,我不能使用“算法”(我真希望我能) 我会在评论中澄清关于我可以做或使用哪些事情的任何疑问。

【问题讨论】:

  • 当一个迭代器到达末尾而另一个没有在 while 循环中时,我认为您无法正确处理这种情况。当循环结束时,可能仍会在任一映射中留下一些元素,这些元素可以包含在 U 中。
  • 确实,我想过在 while 循环中评估 """it1 != last1 and it2!= last2""" ,然后解析剩余的元素,但这取决于哪个先结束
  • 当前代码有什么问题?它会带来错误的结果吗?它会崩溃吗?
  • 我会在发现错误时编辑循环,但现在它只会给出错误的结果

标签: c++ algorithm set-intersection set-union


【解决方案1】:

在主 while 循环之后添加这两个循环。

while (it1 != Kmer1.cend()){
    U += it1->second;
    it1++;
}
while (it2 != Kmer2.cend()){
    U += it2->second;
    it2++;
}

【讨论】:

  • 我之前考虑过。如果 Kmer1 的最后一个元素等于 Kmer2 的最后一个元素(反之亦然),则不会比较最后一个元素,因此它将无效
  • @PaueteGalopa 我真的看不出现在你的循环是如何设计的。能举个例子吗?
  • 对不起,它确实有效!我不敢相信事情就这么简单。我以前尝试过,但现在我看到了以前使用的代码,我明白了为什么没有用。我想我被滥用了以至于我没有看到它......再次感谢
【解决方案2】:

这是一个相当简单的解决方案,只使用std::map 的一些属性,没有迭代器。我希望你被允许使用这种解决方案。

#include <iostream>
#include <map>
#include <string>

int main () {
    std::map <std::string, int> A = {{"AAB", 1}, {"AAC", 1}, {"AAG", 3}};
    std::map <std::string, int> B = {{"AAG", 1}, {"AAT", 2}, {"ABB", 1}};

    std::map <std::string, int> Union;
    int sum_A = 0, sum_B = 0, sum_Union = 0, sum_Inter = 0;;

    for (auto &x: A) {
        Union[x.first] = std::max (Union[x.first], x.second);
        sum_A += x.second;
    }
    for (auto &x: B) {
        Union[x.first] = std::max (Union[x.first], x.second);
        sum_B += x.second;
    }   
    for (auto &x: Union) {
        sum_Union += x.second;
    }
    sum_Inter = sum_A + sum_B - sum_Union;
    double distance = 100.0 * (1.0 - double(sum_Inter)/sum_Union);

    std::cout << "sum_Union = " << sum_Union << " sum_Inter = " << sum_Inter << "\n";
    std::cout << "Distance = " << distance << "\n";
}

【讨论】:

  • 虽然需要额外的地图。并且是 O(n log n) 而 OP 有 O(n) 解决方案
  • @Jarod42 有效。除了相当大的 n 值外,它应该保持高效。考虑到上下文(大学),我试图找到最简单的解决方案
【解决方案3】:

这个循环应该可以工作:

while ( true ){
    bool end1 = it1 == Kmer1.cend();
    bool end2 = it2 == Kmer2.cend();
    if( end1 and end2 )
        break;

    if( end2 or it1->first < it2->first ) {
        U += (it1++)->second;
        continue;
    }
    if( end1 or it2->first < it1->first ) {
        U += (it2++)->second;
        continue;
    }
    auto p = std::minmax( (it1++)->second, (it2++)->second );
    I += p.first;
    U += p.second;
}

【讨论】:

    【解决方案4】:

    对于unordered_mapping,一个稍微干净的方法,但仍然适用于mapping,是将Kmer1 的所有元素添加到U,并将共享元素添加到I。然后将Kmer2的所有未共享元素添加到U

    for(it1 = Kmer1.cbegin(); it1 != Kmer1.cend(); it1++) {
        auto other = Kmer2.find(it1->first);
        if(other == Kmer2.cend()) {
            U += it1->second;
        } else {
            U += max(it1->second, other->second);
            I += min(it1->second, other->second);
        }
    }
    for(it2 = Kmer2.cbegin(); it2 != Kmer2.cend(); it2++) {
        if(Kmer1.count(it2->first) == 0) {
            U += it2->second
        }
    }
    

    对于正确实现的unordered_mapping(哈希表),find 操作将是O(1),而不是O(log(n),使其更快一些。

    【讨论】:

    • 您的代码是否考虑了当两个映射中都存在键时,将较高的值添加到U 的要求?
    • @ciamej。非常清楚,是的。第一个循环中的else 是您感兴趣的区域。
    • @PaueteGalopa 它是使表达式工作所需的任何类型,由编译器确定。基本上,我是一个懒惰的黑客。随意用正确的迭代器类型替换它。
    • 啊,好的,现在我明白了;)
    • @PaueteGalopa。这个网站很棒:en.cppreference.com/w/cpp/language/autocplusplus.com也是如此
    猜你喜欢
    • 2016-01-20
    • 2015-04-27
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多