问题描述
我阅读了有关算法时间复杂度的不同主题。我试图理解和学习如何分析算法,但是我仍然有一些担忧。因此,我希望本主题将有助于使它们更清晰。
这是个问题:
给出一个整数 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。上述算法的问题大小是多少?
是n
或log10(n)
(value
或length 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)
哪个是正确的答案?还是两者都不对?
请帮助我澄清一下。
解决方法
- 在两种情况下,问题大小:N。考虑一下,问题的大小是多少?它可以帮助您确定对复杂性有直接影响的因素。它应该独立于您使用的算法,它描述问题,而不是您的解决方案。然后,您使用big-O表示法描述了复杂性,以指示根据问题大小需要进行多少次计算。如果将N放入big-O,则问题大小为N。
您可以将问题的大小设置为所需的大小,只要您与问题一致即可。您可以说问题大小为M = log10(N)。这将改变big-O的复杂度,现在它应该取决于问题的大小,即M。如果以这种方式进行操作,请不要在big-O表示法中使用log10(N)。
O(N)= O(10 ^ M),选择一种表示法并坚持下去。
-
通常,编程语言的方法并不具有恒定的时间复杂度。它们的复杂性应在该语言的参考资料中说明。例如,99%的编程语言具有sort函数,可在O(n log n)时间对数组/列表/集合进行排序。如果您具有对数组进行排序的代码:
sort(array)
您会说它是O(1),因为您使用了该语言提供的方法吗?想一想为什么还要确定复杂性。它供将来使用您的代码的人使用,因此他可以轻松地说出代码将执行多长时间。如果您告诉他代码是O(1),那么如果他放入长度为10 ^ 18的数组并等到他的生命尽头才能完成O(1)代码,他将真的很失望。语言的功能应包括在复杂性中。
预处理应包括在例程的复杂性中。如果您在开始时有一个预处理部分,通常您会说复杂度为O(预处理)+ O(其余部分),至少是在预处理部分比其余部分花费更多时间的情况下。如果需要,也可以将它们合并在一起。在您的情况下,您可以写O(logN)+ O(N)或只写O(N),两者都很好。
- 第一次计算: 我不会在公式中使用“ n”,因为它代表数字“ n”的长度(命名冲突)。估计是正确的,复杂度为O(2 ^ M),其中M = log(N)。
第二个也是正确的。
两个方程式都计算相同的东西,但请注意表示法。第一个数字中的“ n”是数字,第二个数字中的“ n”是数字。您可以使用这两种表示法中的任何一种,只要不将它们混淆即可。坚持下去。