发现算法时间复杂度的问题大小和输入的预处理成本 a如果问题的大小为数字n的长度,则成本分别为: b如果问题大小是数字n的值,则

问题描述

我阅读了有关算法时间复杂度的不同主题。我试图理解和学习如何分析算法,但是我仍然有一些担忧。因此,我希望本主题将有助于使它们更清晰。

这是个问题:

给出一个整数 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。上述算法的时间复杂度是多少?

a。如果问题的大小为数字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。考虑一下,问题的大小是多少?它可以帮助您确定对复杂性有直接影响的因素。它应该独立于您使用的算法,它描述问题,而不是您的解决方案。然后,您使用big-O表示法描述了复杂性,以指示根据问题大小需要进行多少次计算。如果将N放入big-O,则问题大小为N。

您可以将问题的大小设置为所需的大小,只要您与问题一致即可。您可以说问题大小为M = log10(N)。这将改变big-O的复杂度,现在它应该取决于问题的大小,即M。如果以这种方式进行操作,请不要在big-O表示法中使用log10(N)。

O(N)= O(10 ^ M),选择一种表示法并坚持下去。

  1. 通常,编程语言的方法并不具有恒定的时间复杂度。它们的复杂性应在该语言的参考资料中说明。例如,99%的编程语言具有sort函数,可在O(n log n)时间对数组/列表/集合进行排序。如果您具有对数组进行排序的代码:

     sort(array)
    

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

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

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

第二个也是正确的。

两个方程式都计算相同的东西,但请注意表示法。第一个数字中的“ n”是数字,第二个数字中的“ n”是数字。您可以使用这两种表示法中的任何一种,只要不将它们混淆即可。坚持下去。