【问题标题】:What data structure to represent clustered dots within a Hamming space?什么数据结构来表示汉明空间内的聚集点?
【发布时间】:2019-12-05 17:37:17
【问题描述】:

我有一组N 染色体,它们都可以用大小为L 的二进制字符串来表示。 N 通常大小为 1e4(正负两个数量级)。 L 可以变化很大,但可以上升到 1e7。目前,我正在为每个N 染色体使用L 位记录所有这些信息,但这会消耗太多内存。我正在寻找更好的数据结构。

我们可以利用N 染色体不是随机分散在2^L 可能性空间中的事实。它们往往非常聚集。换句话说,平均汉明距离通常远小于 L/2。让我们想象一下对我们的染色体进行 PCA,它可能看起来像这样

在离散的时间步长上,所有N 染色体都被其他N 染色体替换。有“突变”(一个突变一次改变一位)。因此,染色体的种群会进化,以便稍后它们看起来更像这样

现在,问题是N 染色体形成两个(或更多)组并非不可能

另外,一些“后代”染色体可能是从两个“父”染色体“杂交”产生的。如果两条父染色体属于两个不同的组(PCA 图上的组),那么事情就会变得更难一些。例如,假设L=15,我们可能有两条父染色体

[0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0] // Let's call it 'Alice'
[1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1] // Let's call it 'Bob'

“生”出染色体

[0,0,0,1,0,0,0,0,1,0,0,1,1,1,1,1] // This is Charlie, son of a mixture of Alice and Bob 
//           Alice <- ^ -> Bob

在实践中,我们可以有很多这些不同的混合体。杂交种可以进一步杂交。但与 2^L 的可能性相比,参数空间探索仍然非常小,因此应该有一种方法可以不必使用 N*L 位来表示 N 条染色体。

我可以使用什么数据结构来表示N 染色体以最大限度地减少内存使用?

我在想我可以有一些参考染色体,并通过它们与参考染色体携带多少差异来参考所有其他染色体。

我还可以将所有染色体放在 B+ 树的顶端,树的每个分支都列出了它与参考染色体有多少差异。例如,B+ 树的最基础可能是[0,0,0,0,...0,0]。每隔几个时间步,我就可以重新计算整个 B+ 树以进行清理。现在混合的存在将成为这个解决方案的一个问题。另外,我想知道在 B+ 树中是否允许反转突变。也许,我应该允许 B+ 树的某些分支只考虑染色体的子部分(如前 L/5 位)。在所有情况下,我都不确定如何实现所有这些细节(例如如何重新计算 B+ 树以清理它)。

【问题讨论】:

    标签: c++ data-structures tree hamming-distance


    【解决方案1】:

    我的第一种方法是将每个染色体分成大小相等的块。大多数染色体将共享它们的大部分块:您可能有 10000 条长度为 1000 的唯一染色体,但给定的 20 位片段在总体中可能只有少数唯一值。

    我会从一开始就考虑缓存行来设计这个:由于缓存行往往是 64 字节(给或取 2 的因数),您可能希望有大小约为 512 的块,然后代表每个染色体作为一系列指向具体块值的指针。一个 20 块(约 10k 位)的染色体实例将只需要 160 字节(在 64 位机器上),而不是 1.2k 字节。如果您不使用指针而是使用某些数据结构的索引,则更少。您还需要存储每个块的一些不同“变体”,但如果有例如每个块有 10 个变体,您只需要相当于 10 个完整染色体的空间。

    想想看,你不妨把一个块称为Gene - 这很合适。

    这是受到https://en.wikipedia.org/wiki/Flyweight_pattern 的启发。不过,我不会在 OOP 图中迷失方向。除了上面描述的染色体表示之外,您只需要一些存储引用的块值和一种体面的方法来确定突变何时导致唯一的块值添加到存储中(大多数突变 创建唯一值,问题在于何时合并/删除一个值。您可能应该在每个块中包含一小部分空间以用于各种引用计数。)。


    这是一个您可以使用的简单实现:

    #include <array>
    #include <vector>
    #include <cstring>
    #include <set>
    
    // TODO: Magic numbers.
    
    struct alignas(64) Gene
    {
        mutable std::uint32_t refCount;
        std::array<std::uint32_t, 16 - 1> bitStorage;
    };
    
    static_assert(sizeof(Gene) == 64); // Check that there's no padding.
    
    
    struct Chromosome
    {
        std::vector<const Gene*> genes;
    };
    
    
    class World
    {
        struct GeneValueComparer
        {
            bool operator()(const Gene& lhs, const Gene& rhs) const
            {
                return std::memcmp(lhs.bitStorage.data(), rhs.bitStorage.data(), 60) < 0;
            }
        };
    
        std::vector<Chromosome> chroms;
        std::set<Gene, GeneValueComparer> geneBank;
    
        const Gene* addGene(Gene g)
        {
            auto [it, inserted] = geneBank.insert(g);
            if (inserted)
                it->refCount = 1;
            else
                it->refCount++;
            return &*it;
        }
    
        void duplicateChromosome(int index)
        {
            for (const Gene* g : chroms[index].genes)
                g->refCount++;
        }
    
        void deref(const Gene& g)
        {
            g.refCount--;
            if (g.refCount == 0)
                geneBank.erase(g);
        }
    
        Gene extractGene(const Gene& g)
        {
            deref(g);
            return g;
        }
    
        void flipOne(std::size_t chromToFlip)
        {
            for (const Gene* g : chroms[chromToFlip].genes)
            {
                Gene modifiable = extractGene(*g);
                for (auto& val : modifiable.bitStorage)
                    val = !val;
                addGene(modifiable);
            }
        }
    };
    

    https://godbolt.org/z/VaTd9j

    注意复制一条染色体的成本是多么令人讨厌!

    我选择了std::set 来存储当前存在的所有基因。我不知道这是否是最好的选择——这是保证所有基因唯一性的一种优雅方式,但比较(最多)每次比较的全基因长度可能很慢。允许一些重复的基因然后偶尔合并它们可能会得到回报。此外,由于每个Gene 都是单独分配的,因此任何分配开销最终都会出现在不同的缓存行中,这是次优的。这大概是最能改进的部分了。

    【讨论】:

      猜你喜欢
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 2020-12-18
      • 1970-01-01
      相关资源
      最近更新 更多