给定一个未排序的二进制数组,计数 1 的数量,其中只允许检查整个子数组是否全部为零

Given an unsorted binary array, count number of 1's, where allowed only to check if an entire subarray is all zeros

提问人:Gulzar 提问时间:6/30/2022 最后编辑:Gulzar 更新时间:6/30/2022 访问量:284

问:

给定一个未排序的二进制数组,唯一允许的操作是 ,当数组的所有元素均为 0 时,它将返回 。
其复杂性在于
aall_zeros(a)Trueall_zeros(a)o(len(a)) + large overhead constant

我想找到所有包含 1 的索引,尽可能少地运行 all_zeros 一个合理的子问题是假设 1 的数量比 0 的数量“小得多”(比如 x100~x1000)。


从理论上讲,这可以通过迭代数组元素并测试来解决。
在实践中,开销常数迫使我们尽可能大批量地工作。我们不能假设知道数组中 1 的比率,但如果某些算法需要该知识,请分享它。
all_zeros([element])

我正在寻找一个概念性的解决方案,因此我没有指定开销常数与计算时间之间的比率。all_zeros

请注意,我正在寻找一个普通情况的解决方案,而不是一个最坏的情况解决方案。
现在需要定义 1 和 0 的概率分布,但我试图将其保持在较高水平,我不会深入探讨细节,同时仍然保持可回答性。
可能有一个最好的解决方案,它总是能获得最小的开销。如果有,它将被接受。

数组 算法 :时间复杂度 、与语言无关 的开销

评论

3赞 trincot 6/30/2022
如果所有零碰巧都是孤立的,就像在11010101101011011010101中一样,你只能调用每个单独的数字。all_zero
0赞 collapsar 6/30/2022
@trincot 虽然这是真的,但该示例违反了问题约束,即数组在元素中(非常)稀疏。1
1赞 trincot 6/30/2022
@collaspar,“违反”是一个非常强烈的词,表示表示为“一个合理的子问题”的东西,当提问者说“我们不能假设知道数组中 1 的比率”时更是如此。
0赞 Gulzar 6/30/2022
@trincot虽然你的评论都是正确的,但这是“微不足道的情况”,这在统计学上也是非常不合理的,即使假设在 [0, 1] 上均匀分布。但更重要的是,我认为这显然不是问题所在。让我们添加一个假设,即 X10 总是比 0 少 1。
0赞 trincot 6/30/2022
你能编辑你的问题吗?因为这个假设与你在问题中写的内容相矛盾(“我们不能假设......”)。

答:

1赞 MrSmith42 6/30/2022 #1

我会检查大块,如果它们不为零,我只尝试较小的块。 根据 1 秒和“大开销常数”的比率,我会选择合适的起始尺寸。

这里是如何检查的想法(通过示例)

数据:(空格仅用于可读性)

   00001110 00100001 00100000 01000000 00000000 00000000 00000101 01010000
1. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   -> both checked intervalls are non-zero -> half them

2. xxxxxxxxxxxxxxxxx XXXXXXXXXXXXXXXXX xxxxxxxxxxxxxxxxx XXXXXXXXXXXXXXXXX
      non-zero           non-zero          zero              non-zero

3. xxxxxxxx XXXXXXXX xxxxxxxx XXXXXXXX                   xxxxxxxx XXXXXXXX
     n-z      n-z      n-z      n-z                        n-z      n-z   

4. xxxxXXXX xxxxXXXX xxxxXXXX xxxxXXXX                   xxxxXXXX xxxxXXXX
   zero n-z n-z n-z  n-z zero n-z zero                   zero n-z n-z zero

5.     xxXX xxXXxxXX xxXX     xxXX                           xxXX xxXX 

...

我希望这个想法是明确的。但我强烈建议分析从哪个块大小开始,以及何时切换为单元素块。

0赞 Matt Timmermans 6/30/2022 #2

如果某个子数组返回 false,则可以在该子数组中进行二进制搜索以查找第一个 1 的位置。此过程不会告诉您有关该 1 之后的任何元素的信息,因此您将在那之后重新开始。all_zeros(a)

问题是进行初始查询的大小。如果每个查询返回 true 的概率为 50%,则查询总数最少。如果初始查询有 50% 的几率找到 1,那么二叉搜索中的所有查询也将有 50% 的几率,如果 1 平均相隔 L 个槽,则每 1 的总成本是对数2 L + 1 个查询。

如果 L 的长度是它应该的两倍,或者是它应该长度的一半,那么每 1 个查询的成本就会增加大约 1 个,当 1 个相距很远时,这是一个相当小的代价。

因此,一个不需要知道 1 的频率就可以开始的非常好的算法是:

  1. 比如说,设置 L=128。这是对 1 个频率的先验估计。
  2. 检查前 L 个元素。如果全部为零,则将 L 乘以 2 并继续数组的其余部分。
  3. 否则,如果 L > 1,则将 L 除以 2,二进制搜索以找到第一个 1 的位置,然后在第一个 1 之后继续数组的其余部分。

如果 1 是随机分布的,则总成本将是每 1 个查询的日志2 L + some_small_number,我认为这是最坏的情况。