给定大小为n的随机可访问输入数据(排序数组),我希望使用参数化分区函数将其分配到存储桶中。我希望结果返回到桶边的索引数组。
分区函数返回一个bool来指示是否值也应该在同一个分区中。
请注意,在分区之前,我们不知道需要多少桶:
...在运行分区算法之前不知道。
作为一个具体的例子,假设我们有分区功能:
sameBucket(a, b) = (a/10 == b/10)
/
是整数除法(向下舍入)。所以
sameBucket(0,1) == yes
sameBucket(1,2) == yes
sameBucket(0,9) == yes
sameBucket(0,10) == no
分区函数告诉我们0
和10
不应放在同一个存储桶中。
为了清楚起见,考虑下面显示的输入数组及其索引(我假设一个"过了结束"索引叫做end):
[1, 3, 7, 14, 90, 91, 92, 93, 95, 99]
0 1 2 3 4 5 6 7 8 9 end
对于此数据,作为新存储桶成员的元素用^
表示:
[1, 3, 7, 14, 90, 91, 92, 93, 95, 99]
0 1 2 3 4 5 6 7 8 9 end
^ ^ ^ ^
如果我使用上面的分区功能,我只会回到开始新桶的索引:
[0, 3, 4, end]
结果数组中的每个索引表示存储桶函数所说的第一个元素与之前的元素不在同一个存储桶中。
0…<end
表示。 0…<3
是数字[1, 3, 7]
,全部为0
,除以10
。 3…<4
是单个数字14
,除以1
时为10
。4…<end
是数字[90, 91, 92, 93, 95, 99]
,除以0
时全部为10
。我认为修改后的二进制搜索应该能够有效地执行此分区。对于n
输入值和b
输出存储桶,运行时间应该最差O(b.log(n))
。有没有人有它的算法,甚至只是名字,所以我可以查找它?
答案 0 :(得分:1)
有效解决此问题需要假设如果排序范围两端的元素属于sameBucket(left, right)
所属的同一个存储桶,那么left
和right
之间的所有值都必须属于也是同一个桶。
我认为修改后的二进制搜索应该能够有效地执行此分区
是的,您可以运行二进制搜索,如下所示:
nextBucket
设为零left
设置为nextBucket
,将right
设置为输入数组的末尾mid
设置为left
和right
sameBucket(nextBucket, mid)
为true
,请将left
移至mid
;否则,请将right
移至mid
left == right
,退出循环;否则,请返回第3步left
是下一个分区索引。 nextBucket
和left
之间的所有项目都在同一个文件夹中。nextBucket
设为left+1
nextBucket
等于n
,您就完成了;否则,请返回第2步。我不认为这个算法有一个特殊的名字 - 它是一个很难伪装的二进制搜索。
答案 1 :(得分:1)
感谢their answer的dasblinkenlight。
我很确定有一种算法可以提供比他们给出的方法更好的大O性能(它确实具有我在OP中提到的asymtotic复杂性)。我昨晚写了它,就像这样。
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
个元素,并且包含b
个不同的存储桶,那么(我相信)算法的运行时复杂性最坏情况 O(b.log(n/b))
。
b == n
,成本在n
中变为线性。b
,当所有桶具有统一大小时,发生最坏情况的复杂性。如果大多数存储桶很小而且很少很大,则运行时复杂性会降低。B
的序列,其中sum(B) == n
的复杂度类似于:O(sum_of_logs_of_B)
。 b-1
存储桶的大小为1,存储区大小为n-b
,则会接近O(b + log(n))
这似乎是一个很好的复杂行为。它适应内容的细节,并且在输入大小方面具有绝对最差的时间性能线性。
我不知道是否有这个名字 - 我非常怀疑它是原创的!如果你知道这个名字,我想知道它是什么。
算法所需的空间复杂度(不是它的输入或输出)是O(log(n))
,因此输入和输出的线性存储不堪重负。