【问题标题】:What Mathematical Technique is used to compare algorithm complexity?使用什么数学技术来比较算法复杂度?
【发布时间】:2011-04-02 06:35:19
【问题描述】:

我今天开始阅读“算法简介”,但我对其中的一个练习感到有些困惑。

练习1.2.2问读者

假设我们正在比较合并排序和插入排序的实现 机器。对于大小为 n 的输入,插入排序以 8n^2 步运行, 而归并排序以 64n log n 步运行。

对于 n 的哪些值,插入排序优于合并排序?

我首先尝试打开 Wolfram Alpha 并用它来绘制方程图,但我无法准确地比较这两个图。

然后我尝试为 n (200) 选择一个随机值,在纸上计算方程,然后根据我的结果修改 n 的值。
但这花了太长时间。

解决这个练习的正确方法是什么?

【问题讨论】:

    标签: algorithm math


    【解决方案1】:

    由于这不是代数不等式,因此没有分析方法可以找到n in 的解决方案

    (1)   8n² < 64n log_2 (n)
    

    所以你需要近似它。有很多数值方法可以做到这一点,所以我们将使用一个快速的 Python 脚本来做到这一点。

    首先,请注意 Python 的 math.log 函数是以 e 为底的对数,因此首先您需要使用标识将原来的以 2 为底的对数转换为以 e 为底的对数

    (2)   log_2 (n) = log_e (n) / log_e (2)
    

    (Proof.)

    在 (1) 中使用 (2) 并简化收益率

    (3)   n / log_e (n) <= 8 / log_e (2) 
    

    最后,在脚本中评估 (3) 上的 n

    from math import log as ln
    
    n = 2 # Since f(n) = n/ln (n) is not defined for n < 2.
    
    while (n/ln (n) <= 8/ln (2)):
        n += 1
    
    print (n)
    

    输出n = 44,这意味着44满足(1)的最小值。所以n &lt;= 43是满足(1)的条件,也就是说插入排序胜过归并排序,而待排序的数组最多有43个元素。

    【讨论】:

      【解决方案2】:

      求解方程8n² &lt; 64n lg n,我们得到方程2^(n/8) - n &lt; 0

      有一次我没有参加测试,只是做一些练习,我使用了一个工具来查看f(n) = 2^(n/8) - n 的图表。

      正如这里所质疑的,插入排序与合并排序具有相同时间的n 的自然数不存在,对于f(n) &lt; 0,我们得到:

      2

      我希望它可以帮助某人:)

      【讨论】:

        【解决方案3】:

        对于 n·43、8n2·64n log n 和插入排序优于合并排序 尽管合并排序在 £(n log n) 最坏情况下运行,而插入排序在 £(n2) 最坏情况下运行 时间,插入排序中的常数因子使得小 n 更快。因此,使用它是有意义的 当子问题变得足够小时,合并排序中的插入排序。考虑修改 合并排序,其中大小为 k 或更小的子数组(对于某些 k)不进一步划分,而是排序 显式地使用插入排序。

        【讨论】:

          【解决方案4】:

          here:8n2 = 64n log2n我>。只需将这两件事放在一个等式中即可。

          即,大致 n = 43 是插入排序在这里有用的极限。

          通常你可以通过解上面的方程 f(n) = g(n) 来解决这个问题求解 f(n) - g(n) = 0,但是这种情况下的解析结果是'不漂亮,因为您将多项式与对数函数混合在一起。我只是尝试一些值,看看结果从正面变为负面的位置。一旦你有了一个正点和一个负点,你就可以使用二分法来缩小范围。

          蛮力的方法是简单地尝试所有 n 直到某一点。您已经知道 O(n2) 算法不适合大型数据集,因此 n 必须非常小。对于我的测试,它看起来像这样:

          PS Home:\> function lb($n){[math]::Log($n)/[math]::Log(2)}  # binary logarithm
          PS Home:\> 1..80 | %{,($_,(8*$_*$_),(64*$_*(lb $_)))} | %{"{0}: delta={3}, I={1}, M={2}" -f $_[0],$_[1],$_[2],($_[2]-$_[1])}
          ...
          38: delta=1210,9597126948, I=11552, M=12762,9597126948
          39: delta=1024,36393828017, I=12168, M=13192,3639382802
          40: delta=824,135922911648, I=12800, M=13624,1359229116
          41: delta=610,216460117852, I=13448, M=14058,2164601179
          42: delta=382,549232429308, I=14112, M=14494,5492324293
          43: delta=141,080604940173, I=14792, M=14933,0806049402
          44: delta=−114,240561917371, I=15488, M=15373,7594380826
          45: delta=−383,463082570537, I=16200, M=15816,5369174295
          46: delta=−666,633601368154, I=16928, M=16261,3663986318
          47: delta=−963,796734153668, I=17672, M=16708,2032658463
          48: delta=−1274,99519778461, I=18432, M=17157,0048022154
          ...
          

          (请原谅糟糕的代码;这只是一个非常快速的涂鸦。)

          【讨论】:

          • 你为什么假设 log 为 base 2?
          • 对数并不重要,因为他展示了正在使用的技术(在一个方程中使用两者)。
          • @Rekin:合并排序将列表分成两个子列表,分别排序然后合并。哪个对数底在您看来更合适?
          • @Hamish:有点错误的原因。是的,对于谈论复杂性类别,给出一个特定的基数是没有用的,因为常数因素被忽略了,但在这种情况下,我们谈论的是实际的性能数据(确切的步骤数)。虽然那些遵循复杂性类别,但它们仍然有更多细节,并且要回答手头的问题,我们实际上需要使用正确的对数——在这种情况下,底数为 2,但这不是因为数字的二进制表示而是 Mergesort 工作原理的产物。
          • @Hamish:如果您阅读了您发布的链接,它很清楚地说 algorithm 带来了对数基数 2,并且没有提到数字的二进制表示全部。
          【解决方案5】:

          是否存在两个相等的 n 值?

          超过该点会发生什么?下面?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-07-31
            • 1970-01-01
            • 2022-01-13
            • 1970-01-01
            • 2011-08-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多