设计一个采用 O(n log n) 确定的分治算法

Posted

技术标签:

【中文标题】设计一个采用 O(n log n) 确定的分治算法【英文标题】:design a divide and conquer algorithm that takes O(n log n) determinations 【发布时间】:2015-12-03 11:58:09 【问题描述】:

我得到一组球,我的最终目标是找出至少一半的球是相同颜色的。我每次只能挑两个球,判断它们是否相同颜色。那么如何设计一个分治算法来解决这个问题呢?如果有人对此问题有任何想法,非常感谢!

【问题讨论】:

我认为这个问题需要编辑以包含对“查询”一词的更好解释。具体来说,查询的功能和限制是什么? 感谢您的提示,我做了一点改动,希望现在容易理解。 @JasonLee 你有固定数量的颜色吗?可以以某种方式订购它们吗?或者只比较平等。 很遗憾,它们只能进行相等比较... 【参考方案1】:

也许你可以倒着做——如果你不知道n(log n) 比较中的答案,那么只有不到一半的球是相同颜色的。对它们进行合并排序分组...

r g r b r y r r  // worst case arrangement

rg rb ry rr      
    ↓            // 3 * (n / 4) comparisons
rr gb rrr y      
    ↓            // 3 * (n / 8) comparisons
rrrrr gby

【讨论】:

好像是我想要的,非常感谢!【参考方案2】:

我们可以将您的问题简化为以下问题。

如果给定集合中的球按颜色分组,您希望找出最大的组是否至少是集合大小的一半。


这更容易递归解决。 (这个问题不针对空集定义,单独处理。)

class Group  // with appropriate constructors
    int size;
    Color color;


Group findLargestGroupWithSameColors(Set<Ball> ballSet) 
    if (ballSet.size() > 1) 
        // Divide set into two (preferably equal) sets.
        Group first = recursive call on first half.
        Group second = recursive call on second half.
        if (first.color.equals(second.color)) 
            return new Group(first.color, first.size + second.size);
         else 
            if (first.size > second.size) 
                return first;
             else 
                return second;
            
        
     else 
        return single element's color and size = 1
    

祝你好运

【讨论】:

如果你只返回最大的组,我担心,在某些配置中,你会错过可能通过随后合并较小集而形成的最大组。 例如A B A B A C A C. 在 2 个元素集 (AB)(AB)(AC)(AC) 上,您的算法总是返回第二个元素 (2xB)(2xC),完全忽略 As。 与我想要的有点不同...谢谢!【参考方案3】:

我假设您可以对颜色进行排序(例如,您可以将颜色的散列计算为可以排序的整数;我想不出任何不能散列的数据类型)。然后你可以简单地在O(n log n)时间按颜色对球进行排序,然后在排序后的集合中扫描一次,确定连续相同颜色的球的运行。你的答案是,最大跑的球数是否 >= 球数。

编辑

问题实际上是 O(n)。对 n 个球使用具有 O(1) 插入的哈希表。每当你已经拥有它时,增加哈希表组中元素的计数,并在其他地方跟踪最大的组计数。

您甚至可以在最大计数达到 n/2 时提前退出,这应该是随机集平均运行时间的一半。

编辑2

O(n^2) 的示例证明示意图

我坚信,当只允许相等比较时,没有 O(n log n) 解决方案。看下面的例子,应该是 true,正好有一半是 As,其余的都不一样:

第一次划分

n = 16
AAAAAAABCDEFGHIA
AAAAAAAB CDEFGHIA
AAAA AAAB CDEF GHIA
AA AA AA AB CD EF GH IA

现在征服。我们需要在每个征服步骤中找到所有组,因为每个组都可能与另一个大组合并,因此它是所有组的大多数。

在这个例子中,int 左半边 A 显然是赢家,但我们需要在右半边再增加一个 A。由于在分而治之的情况下,右半部分不知道左半部分,因此在最终合并之前,右侧也会尝试找到最大的组,以 n/2 个大小为 1 的组结束。

在下面的符号中,我在字母前使用一个数字来表示找到的那个大小的组。

2A 2A 2A 1A1B 1C1D 1E1F 1G1H 1I1A  1+1+1+1 +1+1+1+1 =4+4 =8
4A       3A1B 1C1D1E1F  1G1H1I1A   1*1+2*1 +2*2+2*2 =3+8 =11
7A1B          1C1D1E1F1G1H1I1A     1*2     +4*4     =2+16=18
8A1B1C1D1E1F1G1H1I                        2*8            =16
                                           53    
n log2 n = 16*4=64

在右侧,我注意到合并组所需的比较次数。要将包含 x 组的集合和包含 y 组的集合合并,您需要 O(x y) 比较,当这两个集合不相交时需要(即,将一组中的每一组与另一组中的每一组进行比较)。 本例需要 53 次比较,低于 64 的 n log2 n。 左侧的比较表现得非常线性。如果你分析你得到的模式(对于 n>7)

Log2(n)-2+ Sumi=0..Log2(n)(2^i) = n/2 - 3 + Log2(n)

但是等等,在正确的比较次数上有一个平方项。让我们检查一下。每行(最后一次合并除外)都将前一行翻倍,并以 (n/4)^2 比较结束。这给了

Sumi=0..Log2(n)-2( (n/4)^2 (1/2)^i ) = 1/8 (n^2 - 2*n)

因此,确实,通过这种分而治之的方法,我们最坏情况下的比较次数是 O(n^2),这似乎是合乎逻辑的。如果所有条目都不同,并且每次只能测试两个是否相等,则需要对每个条目进行测试以查找是否真的没有对。

不知道我是否遗漏了什么,但是当只允许comarisons时,这个问题似乎在O(n log n)中无法通过分而治之来解决。

【讨论】:

其实是算法题,所以有限制。我认为你在现实世界的项目中给出了正确的解决方案,还是谢谢你!

以上是关于设计一个采用 O(n log n) 确定的分治算法的主要内容,如果未能解决你的问题,请参考以下文章

复杂度为 O(n log n) 的分治算法

五种常用的算法设计技巧之二:分治算法

归并排序

算法排序02——归并排序介绍及其在分治算法思想上与快排的区别(含归并代码)

算法笔记:分治

有缺陷的棋盘问题——寻找伪代码算法(分治法)