我正在研究遗传算法项目,我用二进制字符串对染色体进行编码,其中每个32位代表一个值。问题是我正在优化的函数有超过3000个参数,这意味着我的位串中有超过96000位,我对此做的操作只是为了减慢...
我进行了如下操作:我有一个二进制类,我正在创建一个表示我的大二进制字符串的布尔数组。然后我用各种移动和移动等操纵这个二进制字符串。
我的问题是,有更好的方法吗?速度只是杀戮。我敢肯定,如果我只在一个字符串上执行此操作会很好,但我必须对超过1000代的25位字符串进行操作。
答案 0 :(得分:9)
我要做的就是后退一步。您的分析似乎与实现细节紧密相关,即您选择bool []作为表示一串位的方式。
清除bool和数组的思路,并制作一个完整的列表,列出您实际需要执行的操作,它们发生的频率以及它们的速度。理想情况下,请考虑您的速度要求是平均速度还是最差情况速度。 (有许多数据结构通过对每千个廉价操作进行一次昂贵的操作来获得高平均速度;如果任何昂贵的操作是不可接受的,那么你需要事先了解它。)
获得该列表后,您就可以研究哪些数据结构运行良好。
例如,假设您的操作列表是:
鉴于操作列表,我认为您想要的数据结构是 catenable deque 。可缠绕的deques支持在任一端快速插入,并且可以有效地分解成两个deques。然后轻松地将东西插入双端队列中间:打破双端,将项目插入一半的末尾,然后再将它们连接起来。
但是,如果您再对该问题添加另一个操作,例如,“在90000位序列中的任意位置搜索特定位字符串,并在次线性时间内找到结果”,那么只需catenable deque不会这样做。搜索双端队列很慢。还有其他数据结构支持该操作。
答案 1 :(得分:2)
如果我理解正确,你在bool[]
编码位数组。第一个明显的优化是将其更改为int[]
(或甚至long[]
)并尽可能利用整个机器字上的位操作。
例如,这会使轮班效率提高约4倍。
答案 2 :(得分:1)
BitArray课程没有帮助吗?
答案 3 :(得分:0)
BitArray
可能比布尔数组快,但你仍然无法获得内置支持来移动96000位。
GPU非常适合大规模位操作。也许Brahma,CUDA.NET或Accelerator可以提供服务吗?
答案 4 :(得分:0)
让我明白一下;目前,您正在为“染色体”使用一系列32位值。我们在谈论DNA染色体还是神经进化算法染色体?
如果是DNA,你会处理4个值; A,C,G,T。它可以用2位编码,使一个字节能够容纳4个值。您的3000个元素的染色体序列可以存储在750个元素的字节数组中;那没什么,真的。
您的两个最昂贵的操作是来自压缩比特流。我建议使用字节键控枚举:
public enum DnaMarker : byte { A, C, G, T };
然后,通过一次操作将其中的4个转换为一个字节:
public static byte ToByteCode(this DnaMarker[] markers)
{
byte output = 0;
for(byte i=0;i<4;i++)
output = (output << 2) + (byte)markers[i];
}
...并用这样的东西解析它们:
public static DnaMarker[] ToMarkers(this byte input)
{
var result = new byte[4];
for(byte i=0;i<4;i++)
result[i] = (DnaMarker)(input - (input >> (2*(i+1))));
return result;
}
使用四个参数(必要时输出)与在堆中分配和使用数组相比,您可能会看到性能略有提升。但是,你失去了使代码更紧凑的迭代。
现在,因为你将它们打包成四字节“块”,如果你的序列长度并不总是四的精确倍数,你最终会用零值“填充”块的末尾(A )。解决这个问题很麻烦,但如果你有一个32位整数告诉你标记的确切数量,你可以简单地丢弃你在字节流中找到的任何东西。
从这里开始,可能性无穷无尽;您可以通过简单地在每个数组上调用ToString()将枚举数组转换为字符串,同样,您可以通过使用Enum.Parse()迭代来获取字符串并获取枚举数组。
永远记住,除非内存非常重要(通常不是这样),否则以易于使用的格式处理数据几乎总是更快,而不是最紧凑的格式。一个很大的例外是网络传输;如果你必须通过互联网发送750字节而不是12KB,那么在较小的尺寸上有明显的优势。