我最近接受了采访,并被要求查找提供的整数位数。我有这样的事情:
#include <iostream>
using namespace std;
int givemCountOnes (unsigned int X) {
int count =0;
while (X != 0 ) {
if(X & 1)
count++;
X= X>>1;
}
return count;
}
int main() {
cout << givemCountOnes (4);
return 0;
}
我知道有更好的方法,但这不是问题。
问题是,该计划的复杂性是什么?
由于它是输入中的位数,人们说这是O(n),其中n是输入中的位数。
但是我觉得因为上限是sizeof(unsigned int)
,即64位,我应该说顺序是o(1)。
我错了吗?
答案 0 :(得分:5)
复杂性为O(N)。复杂性随着所用类型(unsigned int
)的大小线性增加。
上限无关紧要,因为它可以在将来的任何时间延长。它也无关紧要,因为总有一个上限(内存大小,宇宙中的原子数),然后一切都可以被认为是O(1)。
答案 1 :(得分:2)
我将为上述问题添加更好的解决方案。
在循环中使用以下步骤
x = x & (x-1);
这将一次删除最右边的第一位。
所以只要有一个ON位,你的循环就会达到最大值。当数字接近0时终止。
因此,复杂性从O(int中的位数)改变为O(on位数)。
答案 2 :(得分:0)
O符号用于表示n
的不同值之间的差异。在这种情况下,n
将是位数,因为(在您的情况下)位数将改变执行计算所需的(相对)时间。所以O(n)是正确的 - 一位整数将占用1个单位时间,32位整数将占用32个单位时间,而64位整数将占用64个单位时间。
实际上,您的算法不依赖于数字中的实际位数,而是数字中设置的最高位数,但这是另一回事。但是,由于我们通常将O称为“最坏情况”,它仍然是O(n),其中n是整数中的位数。
我无法想到任何比O更好的方法 - 我可以想到改善循环中迭代次数的方法(例如使用256个条目表,并处理8个一次比特),但它仍然是“更大的数据 - >更长的时间”。由于O(n)和O(n / 2)或O(n / 8)都是相同的(在后一种情况下总时间是1/8而不是第一种情况)。
答案 3 :(得分:0)
Big O表示法描述了最坏情况下的算法步骤数。在这种情况下,当最后一位有1时。因此,当您将n位数作为输入传递时,将有n次迭代/步骤。
想象一个类似的算法,它在列表中搜索1的计数。它的复杂性是O(n),其中n是列表长度。根据您的假设,如果您始终将固定大小列表作为输入传递,则算法复杂度将变为O(1),这是不正确的。
但是,如果你修改算法中的位长:即for (int i = 0; i < 64; ++i) ...
之类的东西,那么它将具有O(1)复杂度,因为它执行O(1)操作64次,你可以忽略常量。否则O(c * n)是O(n),O(c)是O(1),其中c是常数。
希望所有这些例子都有所帮助。顺便说一句,有O(1)解决方案,我会在记得时发布:)
答案 4 :(得分:0)
应该清除一件事:整数运算的复杂性。在这个例子中不清楚,因为你在int
工作,这是你机器上的自然字大小,它的复杂性似乎只有1。
但O-notation涉及大量数据和大型任务,比如你有n位整数,其中n约为4096左右。在这种情况下,复杂性加法,减法和移位至少具有O(n)复杂度,因此您应用于此类整数的算法将是O(n²)复杂度(应用O(n)复杂度的n个操作)。
直接计数算法没有整数移位(假设一位测试是O(1))给出O(n log(n))复杂度(它涉及log(n)大小整数上最多n次加法)。
但是对于固定长度数据(它是C的int),大O分析根本就没有意义,因为它基于变量长度的输入数据,比如更多,几乎任何长度的数据都是无穷大。 / p>