【问题标题】:Simple dictionary in C++C++中的简单字典
【发布时间】:2013-02-15 14:02:36
【问题描述】:

将一些代码从 Python 移动到 C++。

BASEPAIRS = { "T": "A", "A": "T", "G": "C", "C": "G" }

思维导图可能有点矫枉过正?你会用什么?

【问题讨论】:

  • 为什么地图可能是矫枉过正?
  • 你打算用它们做什么?
  • 我可以在类定义中以某种方式将映射定义为具有基值的常量吗?
  • @WHOEVENCARES 为什么你不能?
  • @user1881400 实际上是遗传。:D

标签: c++ map dictionary


【解决方案1】:

您可以使用以下语法:

#include <map>

std::map<char, char> my_map = {
    { 'A', '1' },
    { 'B', '2' },
    { 'C', '3' }
};

【讨论】:

    【解决方案2】:

    如果您正在优化,并且假设输入始终是四个字符之一,那么下面的函数可能值得一试作为地图的替代品:

    char map(const char in)
    { return ((in & 2) ? '\x8a' - in : '\x95' - in); }
    

    它的工作原理是您正在处理两个对称对。条件用于区分 A/T 对和 G/C 对('G' 和 'C' 恰好有第二个最不重要的共同位)。其余算法执行对称映射。它基于以下事实:a = (a + b) - b 对于任何 a,b 都是正确的。

    【讨论】:

    • @WHOEVENCARES 我不确定它是否会比 Benjamin Lindley 提出的纯条件条件更快。但是,至少我的函数的减法部分可以在向量寄存器中并行执行多个字符。
    【解决方案3】:

    虽然使用std::map 很好,或者使用256 大小的char 表也很好,但您可以通过简单地使用enum 为自己节省大量空间。如果你有 C++11 的特性,你可以使用enum class 进行强类型:

    // First, we define base-pairs. Because regular enums
    // Pollute the global namespace, I'm using "enum class". 
    enum class BasePair {
        A,
        T,
        C,
        G
    };
    
    // Let's cut out the nonsense and make this easy:
    // A is 0, T is 1, C is 2, G is 3.
    // These are indices into our table
    // Now, everything can be so much easier
    BasePair Complimentary[4] = {
        T, // Compliment of A
        A, // Compliment of T
        G, // Compliment of C
        C, // Compliment of G
    };
    

    使用变得简单:

    int main (int argc, char* argv[] ) {
        BasePair bp = BasePair::A;
        BasePair complimentbp = Complimentary[(int)bp];
    }
    

    如果这对您来说太过分了,您可以定义一些帮助程序来获得人类可读的 ASCII 字符并获得碱基对恭维,这样您就不必一直进行(int) 强制转换:

    BasePair Compliment ( BasePair bp ) {
        return Complimentary[(int)bp]; // Move the pain here
    }
    
    // Define a conversion table somewhere in your program
    char BasePairToChar[4] = { 'A', 'T', 'C', 'G' };
    char ToCharacter ( BasePair bp ) {
        return BasePairToChar[ (int)bp ];
    }
    

    它干净、简单、高效。

    现在,突然之间,您没有 256 字节的表。您也没有存储字符(每个 1 个字节),因此如果您将其写入文件,您可以为每个碱基对写入 2 位,而不是每个碱基对 1 个字节(8 位)。我必须使用将数据存储为每个 1 个字符的生物信息学文件。好处是它是人类可读的。缺点是本来应该是 250 MB 的文件最终占用了 1 GB 的空间。移动、存储和使用是一场噩梦。当然,即使考虑到蠕虫 DNA,250 MB 也慷慨。无论如何,没有人会阅读 1 GB 的碱基对。

    【讨论】:

    • 但这仍然需要线性查找时间来进行 char 到碱基对的转换
    • @perreal 如果您所说的“线性查找时间”是指O(1),那么是的,整个前提是 O(1),并且只需极少的努力即可最大限度地压缩。
    • @perreal 你能解释一下这是线性时间吗?真正感兴趣。
    • @Rapptz,就像BasePair CharToPair(char p) { for (i : 0..3) { if (BasePairToChar(i) == p) return BasePair(i); } }?
    • @ThePhD,我无法理解您的字符对转换。
    【解决方案4】:

    这是地图解决方案:

    #include <iostream>
    #include <map>
    
    typedef std::map<char, char> BasePairMap;
    
    int main()
    {
        BasePairMap m;
        m['A'] = 'T';
        m['T'] = 'A';
        m['C'] = 'G';
        m['G'] = 'C';
    
        std::cout << "A:" << m['A'] << std::endl;
        std::cout << "T:" << m['T'] << std::endl;
        std::cout << "C:" << m['C'] << std::endl;
        std::cout << "G:" << m['G'] << std::endl;
    
        return 0;
    }
    

    【讨论】:

    • 不相关,但您正在过度刷新流。
    • @Rapptz:我想知道你所说的 'overflushing' 是什么意思,直到我发现std::endl 既是\n(换行符)又是std::flush
    【解决方案5】:

    在我真正关心性能之前,我会使用一个函数,它接受一个基数并返回它的匹配项:

    char base_pair(char base)
    {
        switch(base) {
            case 'T': return 'A';
            ... etc
            default: // handle error
        }
    }
    

    如果我关心性能,我会将基数定义为四分之一字节。 0 代表 A,1 代表 G,2 代表 C,3 代表 T。然后我将 4 个碱基打包成一个字节,为了得到它们的对,我只需取补码。

    【讨论】:

      【解决方案6】:

      char数组中的一个表:

      char map[256] = { 0 };
      map['T'] = 'A'; 
      map['A'] = 'T';
      map['C'] = 'G';
      map['G'] = 'C';
      /* .... */
      

      【讨论】:

      • 那是一张很奇怪的地图。
      • 这是一张非常浪费的地图。但是,它得到......完成工作......?
      • @ThePhD,难以置信?使用 256 个字节。 std::map 的开销是多少?查找时间是多少?
      • 你知道这可以通过一个简单的枚举和一个函数来完成吗?如果您想稍微花哨一点,您可以将该枚举用于其他枚举的表中,并且仅使用 4 个字节。如果您真的想研究一些优化齿轮,您可以将 ATCG 打包成 2 位(就像 DNA 最合理的二进制表示所做的那样)。 char 不是 DNA 结构的数据类型(DNA 列表文件在 ASCII 中大 4 倍是有原因的)。
      • @spin_eight,这是一个直接访问表,查找是O(1)
      【解决方案7】:

      这是我能想到的最快、最简单、最小空间的解决方案。一个好的优化编译器甚至会消除访问 pair 和 name 数组的成本。此解决方案在 C 中同样适用。

      #include <iostream>
      
      enum Base_enum { A, C, T, G };
      typedef enum Base_enum Base;
      static const Base pair[4] = { T, G, A, C };
      static const char name[4] = { 'A', 'C', 'T', 'G' };
      static const Base base[85] = 
        { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
          -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
          -1, -1, -1, -1, -1,  A, -1,  C, -1, -1,
          -1,  G, -1, -1, -1, -1, -1, -1, -1, -1, 
          -1, -1, -1, -1,  T };
      
      const Base
      base2 (const char b)
      {
        switch (b)
          {
          case 'A': return A;
          case 'C': return C;
          case 'T': return T;
          case 'G': return G;
          default: abort ();
          }
      }
      
      int
      main (int argc, char *args) 
      {
        for (Base b = A; b <= G; b++)
          {
            std::cout << name[b] << ":" 
                      << name[pair[b]] << std::endl;
          }
        for (Base b = A; b <= G; b++)
          {
            std::cout << name[base[name[b]]] << ":" 
                      << name[pair[base[name[b]]]] << std::endl;
          }
        for (Base b = A; b <= G; b++)
          {
            std::cout << name[base2(name[b])] << ":" 
                      << name[pair[base2(name[b])]] << std::endl;
          }
      };
      

      base[] 是一个快速的 ascii char 到 Base(即 int 介于 0 和 3 之间)的查找,有点难看。一个好的优化编译器应该能够处理 base2() 但我不确定是否有。

      【讨论】:

      • 但此解决方案假定输入为数字 0、1、2、3,而不是 ASCII 字符。您仍然需要执行输入映射,对吗?
      • 好点。我用我能想到的最快的映射和更漂亮但可能更慢的基于开关的映射修复了它。
      • 了解您是否正在使用 C++ 的好提示“最小空间解决方案”是 49 行;)
      【解决方案8】:

      BASEPAIRS = {“T”:“A”,“A”:“T”,“G”:“C”,“C”:“G”} 你会用什么?

      也许:

      static const char basepairs[] = "ATAGCG";
      // lookup:
      if (const char* p = strchr(basepairs, c))
          // use p[1]
      

      ;-)

      【讨论】:

        猜你喜欢
        • 2014-04-11
        • 2021-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多