算法的代价
Cost of an algorithm
问题是下一个:
如果想知道vector的大小,我们不能使用size(),但是我们有一个函数inBounds(vector&arr,int index),如果索引是vector对象的有效位置,则返回true。
我的方法是迭代位置。从1开始,复制(2,4,8,16,32…),直到inBounds返回false,后退一步,在子范围内重复搜索。
举个例子,N = 21:
- 1 = True
- 2 = True
- 4 = True
- 8 = True
- 16 = True
- 32 = False
返回到16,在16-32范围内搜索:
- (16+1) = True
- (16+2) = True
- (16+4) = True
- (16+8) = False
回到20(16+4),重新开始:
- (20+1) = True
- (20+2) = False
重新开始:
- (21+1) = False
好的,那么大小是21
这是我的代码实现:
#include <iostream>
#include <vector>
using namespace std;
int inBounds(const vector<int>& arr,int i)
{
return i < arr.size();
}
int size(const vector<int>& arr)
{
int init = 0;
while (inBounds(arr,init))
{
int start = 2;
while (inBounds(arr,init+start))
{
start *= 2;
}
start /= 2;
init += start;
}
return init;
}
int main()
{
vector<int> arr;
for (int i = 0;i < 1000;i++)
{
arr.resize(i);
int n = size(arr);
if (n != arr.size())
cout << "FAIL " << arr.size() << ' ' << n << endl;
}
return 0;
}
这个效果很好。但我不知道这个算法的确切成本。第一次搜索确实是log(N),但现在我需要添加子范围搜索。所以我对实际成本有疑问
实际上,最坏的情况是O(log(n)2)(这是意料之中的,因为您有一个O(log)
周期嵌套在另一个日志周期中)
要了解原因,请尝试缩小31(一般情况下为2N-1)
我们举个例子,N = 31:
1 = True
2 = True
4 = True
8 = True
16 = True
32 = False
O(log(N))到这里,好吧,没人质疑它
,
现在计算额外的步骤
返回到16,在16-32范围内搜索:
(16+1) = True
(16+2) = True
(16+4) = True
(16+8) = True
(16+16) = False
(4 + 1)步骤——日志(32/2)+ 1 =日志(32)
24岁退一步
(24+1) = True
(24+2) = True
(24+4) = True
(24+8) = False
(3+1)步-即log(16/2)+1=log(16)
28岁退一步:
(28+1) = True
(28+2) = True
(28+4) = False
(2+1)步-即log(8/2)+1=log(8)
30岁退一步
(30+1) = True
(30+2) = False
(1+1)步,即log(4/2)+1=log(4)
结果:(4+3+2+1=正10步+负4步)。或者用另一种形式log(32)+log(16)+log(8)+log(4)+log(2) - 1 =log(32)(1+1/2+1/3+1/4+1/5)-1
。忽略最后的-1,你的公式就变成了像
log(N)*(1+1/2+1/3+1/4+...+1/N)
对于大N-es,调和级数是发散的,渐近行为是对数的。
你得到了一个很好的O(log(N)*log(N))
复杂度。
QED (? ?)
似乎你的第一个子范围搜索也是logN(因为它使用与初始部分基本相同的算法),而你的最终子范围搜索是线性的(迭代每个值),但它非常有限,是n的一小部分。我会说你的算法大约是c * logN,其中c是一个小值,代表你正在做的子搜索的数量,所以一般是O(logN)。
一旦你找到了上界,你真的应该做一个二分查找。应用到您的示例:
- 初始循环的结果:16→true, 32→false,所以size in (16,32]
二分查找总是测试最大的发现为真和最小的发现为假之间的中间值:
- 24→false => (16,24)
- 20 - true => (20,24)
- 22→false => (20,22)
- 21→false => (20,21)
所以尺寸是21
注意,这只需要4个额外的测试,而不是你的算法需要7个。
假设向量的大小为N = 31。由于这将是你的算法的最坏情况,这里是它的工作原理:
first pass : 1,2,4,8,16,32 [ total = 6 ]
second pass: 17,18,20,24,32 [ total = 5 ]
third pass : 25,26,28,32 [ total = 4 ]
forth pass : 29,30,32 [ total = 3 ]
fifth pass : 31 [ total = 1 ]
如果我们用n来表示,它将是:
T(N) = logN*(1+1/2+1/4+1/8+....)
顺序为:(logN)^2
你应该考虑@celtschk给出的方法,即O(logN)
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 基于ELO的团队匹配算法
- C++选择排序算法中的逻辑错误
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- C++A*算法并不总是在路径中具有目标节点
- 排序算法c++
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 算法问题:查找从堆栈中弹出的所有序列
- 下面是排序算法O(n)吗
- KMP算法和LPS表构造的运行时间
- 为什么我的排序算法会更改数组值
- 求最大元素位置的分治算法
- 具有非整数边容量的最大流量的Dinic算法
- 到连接组件算法的问题(递归)
- STL算法函数在多个一维容器上的使用
- 读取最后一行代码算法 - c++ 时出现问题
- 括号更改 O(n) 算法
- std::unordered_map 搜索算法是如何实现的?
- 如何实现高效的算法来计算大型数据集的多个不同值?
- 算法的代价