有一个位表,宽度为一千个元素,行数超过一万行。您需要将行分成一到四个元素的组,其中该组中的每一行在与另一行相同的位置上没有 1 位。
嗯,就是这样。如果宽度为 3 并且有 5 行,
1 0 0
0 1 0
0 0 1
1 0 1
1 1 1
,则输出三组
1)
1 0 0
0 1 0
0 0 1
1 0 1
1 1 1
众所周知,最常见的情况是没有不相交的线,这意味着有多少条线就有多少组。
任务是找到不相交的线并将它们分成四组。只有当由于合适的组溢出而无法堆叠不相交的字符串时,才可以将不相交的字符串最终放入一个字符串的组中。
到目前为止,我只是通过详尽的搜索解决了这个问题https://dotnetfiddle.net/k6tBjL,其中每一行都与其他每一行进行比较(好吧,几乎),这给出了令人不快的性能。
有没有办法降低算法的复杂度并加快这个过程?
对我来说,BitArray 不太适合这项任务。它将 8 个元素打包到 1 个字节中。每次写入时对每个新元素重复编码,因此每次读取时重复解码。这不能不影响算法的性能。
uint在最简单的情况下,32个元素或16个元素的数组可用于存储1000位ulong。也就是说,uint我们在一个元素中放置 32 位数据。比较它们的不相交非常简单:我们还按顺序迭代元素。检查可以在任何方向进行,直到找到交叉点。两个数如果其位不相交,则
a & b运算时返回0,如果a & b大于0,则可以完成顺序检查。在单线程测试中,
uint它比ulong.根据随机数据,这是单线程中的最佳结果。但对于有机数据,通过逻辑 OR 使用组的总价值有可能提高生产率。也就是说,首先将字符串与组掩码进行比较,然后与其余字符串进行比较。
最初按汉明权重对字符串进行排序也可能有所帮助。一些非常善良的人从俄语维基百科中删除了一个页面。
Alexander Petrov 在评论中提供了文档的链接。
Также можно подключить параллельную обработку данных с помощью
Parallel.ForEach. Тогда для храненияBucketследует использоватьConcurrentBag. Но по тестам на dotnetfiddle это, очевидно, не даёт никакого прироста к скорости выполнения. По всей видимости, выполнение идёт также в один процесс, но с многопоточным оверхедом.Так что самый производительный вариант беглых тестов https://dotnetfiddle.net/YuZD30
Скопирую код также сюда.
答案的本质是,即使是完整的搜索也可以通过不同的方式完成。
而提高暴力破解的魔杖就是BitArray(感谢Alexander Petrov的提醒)。
因此我们有 N (10,000) 行和 M (1000) 列。
所以这完全是矫枉过正
N*N*M,但有一个细微差别。解决方案0。由问题中的链接给出。
这里我只使用BitArray来存储行,但没有使用它的操作。我将每一行与每一行进行比较,并且在每次这样的比较中,我用来比较行的单元格。
在我看来, for 更快
BitArray.And,但这仅在理想情况下成立,即单元格中的匹配项位于行的开头。解决方案 1. 使用 比较字符串
BitArray.And。在一般情况下,我们的生产力得到了提高,因为即使它在这里
N*N*M,它*M也很便宜,因为BitArray.And解决方案 2:添加启发式方法。
如何改进
N*N[*M]?您可以稍微减少第一个因素。我们找到 1 数量最多的列,并立即将带有 1 的行放入组中。
接下来,我们只运行剩余的行,并将每一行与组中的所有内容进行比较。
这就是我们得到 的方式
(N-X)*N[*M],如果 X 很大,那么我们就会有明显的增益。解决方案 3. 如果我们让它更便宜怎么
*N办*M?我们转置表并为每一列获取一个 BitArray。我们正在彻底改变算法。我们遍历行,并连接所有具有 1 到 的列
BitArray.Or。接下来,我们运行生成的 BitArray,并将包含 0 的行添加到组中(直到该组已满)。这从根本上加快了执行时间。我选择了这个选项。
代码与解决方案https://dotnetfiddle.net/6PzuIr
当您检查当前组与新元素的交集时,无需将新元素与该组的所有成员进行比较。相反,组可以存储其所有成员的按位或,然后可以将新元素与按位或进行比较。这将减少比较的次数。
但是,在随机生成位序列的特定示例中,所有元素中都会发生交叉,因此组数始终等于元素数,每组中有一个元素。如果您仍然在实际数据上测试算法,其中实际发生不相交的位序列,那么在将每个新成员添加到组中后,后续比较的次数将会减少。
迭代矩阵并对每行的 1 位求和。
!!!比较位总和 1 大于字符串中位数的字符串的交集是没有意义的
原则上,您可以按 Sum[i] 排序并仅与 的行进行比较
Sum[j] <= bitCount - Sum[i]。或者,
map,其中键是位 1 的总和,值是一个存储不相交字符串的数量及其并集的结构。