寻找算法时间复杂度的问题规模和输入预处理成本

Posted

技术标签:

【中文标题】寻找算法时间复杂度的问题规模和输入预处理成本【英文标题】:Problem size and input pre-process costs in finding time complexity of an algorithm 【发布时间】:2020-08-21 21:34:21 【问题描述】:

我阅读了有关算法时间复杂度的不同主题。我尝试理解和学习如何分析算法,但我仍然有一些顾虑。因此,我希望这个话题能帮助他们弄清楚。

这里有个问题:

给出一个整数n (1 ≤ n ≤ 10^18)。从 n 中删除任何位置的数字以创建新数字。从包括 n 在内的数字中找出最大的立方体数。如果没有任何立方体编号,则返回-1

下面是一个解决方案。

let maxCubeNum = -1;

function isCubeNumber(num) 
  let x = Math.round(Math.pow(num, 1 / 3), 0);
  return x * x * x === num;


// generate the subset numbers from n's digits with recursion
function findCubeNumber(digits) 
  if (digits.length == 0)
    return [0];

  // recursively find all subsets without the last digit
  let parts = findCubeNumber(digits.slice(0, -1));

  let nums = [];
  parts.forEach(part => 
    // case 1: with the last digit
    let num1 = part * 10 + digits.slice(-1)[0];
    // case 2: without the last digit
    let num2 = part;

    if (isCubeNumber(num1))
      if (num1 > maxCubeNum)
        maxCubeNum = num1;

    if (num2 > 0 && isCubeNumber(num2))
      if (num2 > maxCubeNum)
        maxCubeNum = num2;

    nums.push(num1);
    nums.push(num2);
  );

  return nums;


function findNumbers(n) 
  maxCubeNum = -1
  let digits = [];

  // split n to digits
  while (n > 0) 
    digits.unshift(n % 10);  // insert to the first position of the list
    n = (n - n % 10) / 10;   // integer division by 10
  

  // generate subset numbers and find the max cube number from those numbers
  findCubeNumber(digits);

  return maxCubeNum;


let ans = findNumbers(4125);
document.write(ans);

请注意,我的意思是分析上述算法以找出其时间复杂度。我不是在寻找更好的算法。

我的问题

1。上述算法的问题规模是多少?

nlog10(n)valuelength of n)?

我很困惑,因为问题可以通过如下简单的解决方案来解决:

findCubeNumber(n) 
  for(i=n; i>0; i--)
    if (i is_subset n and is_cube_num(i))
      return i;

  return -1;

在这种情况下,问题大小显然是n。这样,上述算法的大小为log10(n),因为它是基于数字n的长度。

但如果我们将输入数字的长度作为问题大小,则伪代码的大小将是10^n

这些都会导致时间复杂度不同的结果。那么,哪一个是问题的大小?

2。当我们谈论算法的时间复杂度时,我们是否会包括预处理输入数据的成本?

例如,在上述算法中,我使用 while 循环将数字 n 拆分为数字。我会把它算作计算时间复杂度的一部分吗?

我见过很少的话题,一个使用特定编程语言中的内置函数将数字转换为字符串,将其视为常数成本,并忽略它。对吗?

3。上述算法的时间复杂度是多少?

一个。如果问题大小是数字n的长度,则成本分别为:

2 - findNumbers() 中的前两个赋值

a*n - 一个 while 循环将数字 n 拆分为数字,a 是一个常量,表示 findNumbers() 的 while 循环中的内部运算符。

递归函数的时间复杂度为:

T(n) = 1 + T(n - 1) + 2^n

在哪里,

1 - 停止递归函数的比较运算符

T(n - 1) - 调用自身,大小减小 1。

2^n - forEach 循环步骤通过数字组合创建数字。每个数字都有两种状态:present(带数字)和absent(不带数字)。

通过归纳:T(n) = 2^n * b + n + c (b, c 是常数)

那么整个算法的时间复杂度,

T(n) = 2 + a*n + 2^n * b + n + c = T(2^n + n) = T(2^n)

b.如果问题大小是数字n的值,那么

T(n) = 2 + a*log10(n) + 2^log10(n)*b + log10(n) + c = T(2^log10(n)) = T(2^log2(n )) = T(n)

哪个是正确的答案?还是两个都错了?

请帮我澄清一下。

【问题讨论】:

【参考方案1】:
    问题大小:N,在这两种情况下。想一想,问题的大小是为了什么?它可以帮助您确定对复杂性有直接影响的因素。它应该独立于您使用的算法,它描述的是问题,而不是您的解决方案。然后你就有了大 O 表示法中描述的复杂性,以指示需要根据问题大小进行多少计算。如果你把 N 放在大 O 中,你的问题大小就是 N。

您可以将问题大小设置为您想要的任何大小,只要您与它保持一致即可。你可以说问题的大小是 M = log10(N)。这将改变 big-O 的复杂性,现在它应该取决于问题的大小,即 M。如果这样做,请不要在 big-O 表示法中使用 log10(N)。

O(N) = O(10^M),选择一种符号并坚持下去。

    通常,编程语言的方法没有固定的时间复杂度。它们的复杂性应在语言参考中说明。例如,99% 的编程语言都有一个排序函数,可以在 O(n log n) 时间内对数组/列表/集合进行排序。如果你有这样对数组进行排序的代码:

     sort(array)
    

你会说它是 O(1),因为你使用了语言提供的方法吗?想想你为什么还要确定复杂性。它是为将来使用您的代码的人准备的,因此他可以轻松说出它将执行多长时间。如果你告诉他代码是 O(1),如果他放置一个长度为 10^18 的数组并等到他生命的尽头等待你的 O(1) 代码完成,他会非常失望。语言的功能应该包含在复杂性中。

预处理应该包含在例程的复杂性中。如果你一开始就有预处理部分,通常你会说复杂度是 O(preprocessing) + O(the rest),至少如果预处理部分比其他部分花费更多时间的话。如果需要,您也可以将它们合并在一起。在你的情况下,你可以写 O(logN) + O(N) 或只是 O(N),两者都可以。

    第一次计算: 我不会在公式中使用“n”,因为它代表数字“n”的长度(命名冲突)。估计是正确的,复杂度是 O(2^M),其中 M = log(N)。

第二个也是正确的。

这两个方程计算相同的东西,但要注意符号。第一个中的“n”是位数,第二个中的“n”是数字。只要不混淆它们,您可以使用这两种符号中的任何一种。坚持一个。

【讨论】:

以上是关于寻找算法时间复杂度的问题规模和输入预处理成本的主要内容,如果未能解决你的问题,请参考以下文章

时间复杂度和空间复杂度

选择插入冒泡排序算法分析

P,NP,NPC,NPH问题的区别和联系

算法分析————时间复杂度

算法的时间复杂度

算法 关于算法的时间复杂度