Algorithm 二进制分区将n个已排序的数据点放入`O(b.log(n))中的b个存储桶中`
给定大小为n的已排序的随机可访问输入数据(一个已排序的数组),我想使用参数化分区函数将其分区到bucket中。我希望结果作为桶边的索引数组返回 分区函数返回一个bool,指示太多的值是否应该在同一分区中 请注意,在分区之前,我们不知道需要多少存储桶:Algorithm 二进制分区将n个已排序的数据点放入`O(b.log(n))中的b个存储桶中`,algorithm,Algorithm,给定大小为n的已排序的随机可访问输入数据(一个已排序的数组),我想使用参数化分区函数将其分区到bucket中。我希望结果作为桶边的索引数组返回 分区函数返回一个bool,指示太多的值是否应该在同一分区中 请注意,在分区之前,我们不知道需要多少存储桶: 所有输入都可能放在一个铲斗中 可能每个输入都需要自己的bucket 也许第一个值需要一个bucket,而数组的整个剩余部分都在第二个bucket中 …这在运行分区算法之前是未知的 作为一个具体的例子,假设我们有分区函数: sameBucket(
- 所有输入都可能放在一个铲斗中李>
- 可能每个输入都需要自己的bucket李>
- 也许第一个值需要一个bucket,而数组的整个剩余部分都在第二个bucket中
sameBucket(a, b) = (a/10 == b/10)
其中/
为整数除法(向下舍入)。所以
分区函数告诉我们0
和10
不应放在同一个存储桶中
为了清晰起见,考虑下面显示的这个输入数组及其索引(我假设一个名为end的“过去的结束”索引):
对于该数据,作为新存储桶成员的元素用^
表示:
[1, 3, 7, 14, 90, 91, 92, 93, 95, 99]
0 1 2 3 4 5 6 7 8 9 end
^ ^ ^ ^
如果我使用上面的分区函数,我只会返回开始一个新bucket的索引:
[0, 3, 4, end]
结果数组中的每个索引表示bucket函数所说的第一个元素与它前面的元素不在同一个bucket中
- 整个数据由范围
为假,这是否意味着0…表示。有效解决此问题需要假设,如果排序范围两端的元素根据
属于同一个存储桶,则sameBucket(左、右)
和左
之间的所有值也必须属于同一个存储桶 我相信经过修改的二进制搜索应该能够有效地执行这种分区 是的,您可以运行二进制搜索,如下所示:右
- 将
设置为零nextBucket
- 将
设置为left
并将nextBucket
设置为输入数组的末尾right
- 将
设置为mid
和left
之间的中点right
- 如果
为sameBucket(nextBucket,mid)
,则将true
向左移动到
;否则,将mid
移动到向右
中间
- 如果
,则退出循环;否则,返回步骤3left==right
- 循环完成后,
是下一个分区索引。left
和nextBucket
之间的所有项目都在同一个bucket中left
- 将
设置为nextBucket
left+1
- 如果
等于nextBucket
,则完成;否则,返回步骤2n
我不认为这个算法有什么特别的名字——它是一个伪装得很差的二进制搜索。多亏了dasblinkenlight的帮助 我很确定有一种算法比他们给出的方法(具有我在OP中提到的不对称复杂性)具有更好的大O性能。我昨晚写的,是这样的
时间复杂性 如果输入有bucket_starts(in_inclusive_range: r) { if r.count is 0 or 1 elements { // There are no bucket starts in this range. // Return an empty array. return [] } else { // If `r`'s start & end elements are in the same bucket // then `r` contains no bucket starts. if same_bucket(element_at[r.first_index], element_at[r.last_index]) { // There are no buckets in this range. return [] } else { // `r` has 1 or more buckets in it. Subdivide and find them. // Note that `midpoint` is in both lower & upper ranges. let first_subrange = range(from: r.first_index to: r.mid_index) let second_subrange = range(from: r.mid_index to: r.last_index) return bucket_starts(in_inclusive_range: first_subrange) + bucket_starts(in_inclusive_range: second_subrange) } } }
元素,并且包含n
不同的bucket,那么(我相信)算法的运行时复杂性是最坏情况b
O(b.log(n/b))
- 在每个输入元素落在不同的桶中的极限情况下,
,成本在b==n
中变为线性n
- 对于给定的
,当所有存储桶的大小都相同时,最坏情况的复杂性就会出现。如果大多数存储桶都很小,而少数存储桶很大,那么运行时复杂性就会降低b
- 对于输出桶大小序列
,其中B
,复杂性将类似于:sum(B)=n
李>O(sum\u of_log\u of_B)
- 在极端情况下,
桶的大小为1,而桶的大小大于b-1
,这将接近n-b
O(b+log(n))
该算法所需的空间复杂度(不是输入或输出)是
,因此输入和输出的线性存储已使其不堪重负。我认为您遗漏了一些关键假设。值数组是否已排序?O(log(n))
可传递吗?它也必须是反射性的吗?如果asameBucket
sameBucket(a,b)
也必须为假?是的,排序::-)检查帖子的第二个单词(编辑前在原始剪切中诚实地显示)。关于后面几点,你完全正确……我不确定。我在想也许我应该把sameBucket(a,c)
表达成与sameBucket
Yes类似的形式——sameBucket肯定是可传递的。是的,正如你所描述的,它也是反射性的。感谢您的澄清。任何给定的分区是否像您的示例一样只包含顺序元素?我的最后一点不是关于自反性(这意味着
对于x的所有值都是真的)。我不确定我描述的属性是否有名称。基本上,它不允许空白:如果输入序列被排序,索引sameBucket(x,x)
和low
处的项目都进入一个bucket,它们之间的所有项目也必须进入同一个bucket。如果你的假设是真的,你不能通过遍历项目并记录high
?@AShelly当然可以,但是sinsameBucket(A[i],A]处的索引,在O(N)中解决这个问题吗[i+1])==false
[0, 3, 4, end]
bucket_starts(in_inclusive_range: r) { if r.count is 0 or 1 elements { // There are no bucket starts in this range. // Return an empty array. return [] } else { // If `r`'s start & end elements are in the same bucket // then `r` contains no bucket starts. if same_bucket(element_at[r.first_index], element_at[r.last_index]) { // There are no buckets in this range. return [] } else { // `r` has 1 or more buckets in it. Subdivide and find them. // Note that `midpoint` is in both lower & upper ranges. let first_subrange = range(from: r.first_index to: r.mid_index) let second_subrange = range(from: r.mid_index to: r.last_index) return bucket_starts(in_inclusive_range: first_subrange) + bucket_starts(in_inclusive_range: second_subrange) } } }
- 将