【问题标题】:Time complexity of a powerset generating function幂集生成函数的时间复杂度
【发布时间】:2011-01-07 21:59:06
【问题描述】:

我正在尝试计算我编写的函数的时间复杂度(它为给定的字符串生成 power set):

public static HashSet<string> GeneratePowerSet(string input)
{
    HashSet<string> powerSet = new HashSet<string>();

    if (string.IsNullOrEmpty(input))
        return powerSet;

    int powSetSize = (int)Math.Pow(2.0, (double)input.Length);

    // Start at 1 to skip the empty string case
    for (int i = 1; i < powSetSize; i++)
    {
        string str = Convert.ToString(i, 2);
        string pset = str;
        for (int k = str.Length; k < input.Length; k++)
        {
            pset = "0" + pset;
        }

        string set = string.Empty;
        for (int j = 0; j < pset.Length; j++)
        {
            if (pset[j] == '1')
            {
                set = string.Concat(set, input[j].ToString());
            }
        }
        powerSet.Add(set);
    }
    return powerSet;
}

所以我的尝试是这样的:

  • 让输入字符串的大小为 n
  • 在外部for循环中,必须迭代2^n次(因为设置的大小是2^n)。
  • 在内部 for 循环中,我们必须迭代 2*n 次(最坏的情况)。

1.所以 Big-O 将是 O((2^n)*n) (因为我们放弃了常数 2)......对吗?

而且 n*(2^n) 比 n^2 差。

如果 n = 4 那么
(4*(2^4)) = 64
(4^2) = 16

如果 n = 100 那么
(10*(2^10)) = 10240
(10^2) = 100

2。有没有更快的方法来生成功率集,或者这是否是最优的?

【问题讨论】:

  • 我其实不懂c#,但是我相信string.concat每次调用都要复制字符串,也就是说复杂度其实是O(n^2*2^n)。
  • @Chris,感谢您的意见。

标签: c# algorithm performance analysis time-complexity


【解决方案1】:

评论:

上面的函数是面试问题的一部分,程序应该接受一个字符串,然后打印出字典中的单词,其字母是输入字符串的变位词子集(例如输入:tabrcoz 输出:船,汽车、猫等)。面试官声称 n*m 实现是微不足道的(其中 n 是字符串的长度,m 是字典中的单词数),但我认为您找不到给定字符串的有效子字符串。看来面试官不对。

早在 1995 年我在微软面试时,我被问到了同样的面试问题。基本上问题是实现一个简单的拼字游戏算法。

您用这种生成电源集的想法完全是在错误地吠叫。好主意,显然太贵了。放弃它并找到正确的答案。

这里有一个提示:对字典运行分析传递,构建一个新的数据结构,更适合有效地解决您实际必须解决的问题。使用优化的字典,您应该能够实现 O(nm)。使用更巧妙构建的数据结构,您可能会做得更好。

【讨论】:

  • 谢谢...我能够获得 O(nm) 解决方案,并提交了我的两个解决方案(这样他们就可以看到我在此过程中做了什么)。
  • @Lirik:太棒了。我假设您正在做的是在内部对字典单词进行排序,正如我在此处描述的 blogs.msdn.com/b/ericlippert/archive/tags/scrabble (请注意,我正在解决查找所有宾果游戏的简单问题)。现在,你能做得更好吗? 如果您从规范化的单词列表中构建一个 trie 会怎样? 我很想看看您是否可以提出一个比使用 trie 或证明更快的解决方案至于为什么不可能。
【解决方案2】:

2。是否有更快的方法来生成功率集,或者这是否是最优的?

您的算法是合理的,但您的字符串处理可能需要改进。

string str = Convert.ToString(i, 2);
string pset = str;
for (int k = str.Length; k < input.Length; k++)
{
    pset = "0" + pset;
}

您在这里所做的只是设置一个位域,但使用的是字符串。跳过这个,直接使用变量i

for (int j = 0; j < input.Length; j++)
{
    if (i & (1 << j))
    {

当您构建字符串时,请使用 StringBuilder,而不是创建多个字符串。

// At the beginning of the method
StringBuilder set = new StringBuilder(input.Length);
...
// Inside the loop
set.Clear();
...
set.Append(input[j]);
...
powerSet.Add(set.ToString());

这会改变算法的复杂性吗?不会。但它会显着减少您创建的额外 String 对象的数量,这将为您提供很好的加速。

【讨论】:

  • 你让我意识到我可以只做 set = set + input[j].ToString(),那不是和使用 StringBuilder 一样快吗?
  • 每次执行此操作时都会创建两个额外的字符串:一个包含input[j] 的单个字母,一个用于连接的结果。修改已经存在的对象更快。 (+ 运算符无论如何都会编译为 String.Concat。)另外,请参阅编辑:创建和重用单个 StringBuilder 比创建和丢弃数千个 StringBuilder 更有效。
猜你喜欢
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-30
相关资源
最近更新 更多