我有一个C ++软件,可以执行基于代理的仿真。在模拟中,有Population
个包含Patch
的元素,这些元素包含两个两个Individual
的{{1}}个元素。每个Haplotype
包含12个向量,每个向量都用于追踪不同类型的基因。
Haplotype
但是,实际上,用户永远不会使用一种或两种以上的基因。这意味着这些未使用的向量中的每一个都消耗几个字节(至少两个指针)。对于具有很多基因和很少class Haplotype
{
std::vector<A> genesA;
std::vector<B> genesB;
std::vector<C> genesC;
std::vector<D> genesD;
....
};
的模拟,可以忽略不计。但是,对于很少有基因和很多个体的模拟,这些额外的字节可能开始变得重要。性能(CPU时间和RAM)至关重要。
是否有一个不错的设计模式可以让我处理此问题?是否可以仅根据需要将向量添加到Individual
中?
答案 0 :(得分:2)
我认为无论您做什么,都必须保留一些空间以容纳数据。
向量有可能“浪费”一些未使用的空间。另外-对象本身会占用一些内存,就像您说的那样。
但是,使用数组时,您可以完全控制。
使用std::vector<Gene>[] genes;
,您可以完全使用所需的内存。
但是现在您必须知道哪个索引代表哪种类型(A,B,C ...)。这些信息将再次消耗您的内存...
另外,如果要添加另一个数组,还必须复制并重新分配该数组。
您可以避免使用指针,并使用nullptr
对其进行初始化,从而为每个向量使用分配的内存。然后,您可以根据需要分配它们。
这样,您只会浪费指针的空间。
当前实现它的方式,您将初始化每个向量对象(可能还有一些保留空间)。
答案 1 :(得分:1)
我不知道这是否是您要寻找的东西,但我认为,可以存储一个仅包含有用矢量的矢量,而不是存储仅一个或两个有用的12个矢量(使用多态性)
例如,您为基因类型创建基类:
class Gene
{
// Make it pure virtual (abstract) for example
};
然后,您创建12种不同类型的基因来继承该基类:
class A : public Gene // Gene type number 1
{
// ...
};
...
class L : public Gene // Gene type number 12
{
};
因此,您所有12种基因都是Gene
。
然后,您可以在Haplotype
中按如下方式存储有用的基因:
class Haplotype
{
std::vector<std::vector<Gene*>> genes;
};
这样,您只存储了对载体有用的基因,而没有其他任何东西。当然,这种使用多态的设计意味着您存储指针而不是值。
您可以在Haplotype
中添加一个方法,该方法检索其组件的真实类型(成功的dynamic_cast),以使其从“用户方面”难以察觉。
我不知道此解决方案是否对您有用,但我希望它能有所帮助。
编辑:
如果您使用的是C ++ 17之前的版本,因此不能使用std::variant
,那么我认为这可以作为替代选择。
答案 2 :(得分:0)
如果所有基因类型的大小都大致相同,那么做这样的事情可能是一个胜利:
union Gene
{
A a;
B b;
C c;
D d;
};
class Haplotype
{
std::vector<Gene> genes;
int aEnd, bEnd, cEnd;
public:
A getA(int idx) {
return genes[idx].a;
}
B getB(int idx) {
return genes[idx + aEnd].b;
}
C getC(int idx) {
return genes[idx + bEnd].c;
}
D getD(int idx) {
return genes[idx + cEnd].d;
}
};
只要不关心单个类型的顺序,就可以在恒定的时间内向载体中添加给定类型的新基因(不需要将所有内容推回去)。
例如,如果需要添加一个B,则可以将其移动到第一个C的位置,然后将其移动到第一个D的位置,然后将其推到最后。然后,您将递增bEnd
和cEnd
中的每一个。
例如:
最初:[A1, A2, A3, B1, B2, C1, C2, C3, C4, D1, D2, D3, D4, D5]
在bEnd处添加新的B:[A1, A2, A3, B1, B2, B3, C2, C3, C4, D1, D2, D3, D4, D5]
替换逐出的C:[A1, A2, A3, B1, B2, B3, C2, C3, C4, C1, D2, D3, D4, D5]
在末尾添加逐出的D:[A1, A2, A3, B1, B2, B3, C2, C3, C4, C1, D2, D3, D4, D5, D1]
。
要在恒定时间内删除基因,您将做相反的事情。关键是要保持不变,所有A都在所有B之前,在所有C之前在所有D之前。
此方案可让您以较少的开销摆脱繁琐的工作:每种类型仅一个int(用于存储结束索引),以换取更难看的代码,添加或删除基因时会花费一些额外的时间,并且无法保持每组基因的插入顺序。这些缺点是否值得您决定。
此外,如果类型的大小截然不同,则此设计将招致每个元素的开销,因为联合必须至少与其最大成员一样大。