计算不同数字数量的省时方法
Time-efficient way to count number of distinct numbers
get_number()
返回一个整数。我将调用它 30 次并计算返回的不同整数的数量。我的计划是将这些数字放入一个std::array<int,30>
中,对其进行排序,然后使用std::unique
。
这是一个很好的解决方案吗?有没有更好的?这段代码将成为我的程序的瓶颈。
我认为应该有一个基于哈希的解决方案,但是当我只有 30 个元素时,它的开销可能太大了?
编辑我更改了唯一到不同。例:
{1,1,1,1} => 1
{1,2,3,4} => 4
{1,3,3,1} => 2
我会使用std::set<int>
,因为它更简单:
std::set<int> s;
for(/*loop 30 times*/)
{
s.insert(get_number());
}
std::cout << s.size() << std::endl; // You get count of unique numbers
如果您想计算每个唯一数字的返回时间,我建议您map
std::map<int, int> s;
for(int i=0; i<30; i++)
{
s[get_number()]++;
}
cout << s.size() << std::endl; // total count of distinct numbers returned
for (auto it : s)
{
cout << it.first << " " << it.second<< std::endl; // each number and return counts
}
最简单的解决方案是使用std::map
:
std::map<int, size_t> counters;
for (size_t i = 0; i != 30; ++i) {
counters[getNumber()] += 1;
}
std::vector<int> uniques;
for (auto const& pair: counters) {
if (pair.second == 1) { uniques.push_back(pair.first); }
}
// uniques now contains the items that only appeared once.
使用 std::map
、 std::set
或 std::sort
算法会给你带来O(n*log(n))
的复杂性。对于少量到大量的元素,它是完全正确的。但是您使用已知的整数范围,这为许多优化打开了大门。
正如您(在评论中(所说,整数的范围是已知且简短的:[0..99]
.我建议实现修改后的计数排序。请参阅:http://en.wikipedia.org/wiki/Counting_sort
您可以在执行排序本身时计算不同项目的数量,无需std::unique
调用。整个复杂性将是O(n)
.另一个优点是所需的内存与输入项的数量无关。如果您有 30.000.000.000 个整数要排序,则不需要单个补充字节来计算不同的项目。
即使允许的整数值的范围很大,[0..10.000.000]
说消耗的内存会很低。实际上,优化版本每个允许的整数值可能消耗低至 1 位。这不到 2 MB 的内存或笔记本电脑内存的 1/1000。
下面是一个简短的示例程序:
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <vector>
// A function returning an integer between [0..99]
int get_number()
{
return rand() % 100;
}
int main(int argc, char* argv[])
{
// reserves one bucket for each possible integer
// and initialize to 0
std::vector<int> cnt_buckets(100, 0);
int nb_distincts = 0;
// Get 30 numbers and count distincts
for(int i=0; i<30; ++i)
{
int number = get_number();
std::cout << number << std::endl;
if(0 == cnt_buckets[number])
++ nb_distincts;
// We could optimize by doing this only the first time
++ cnt_buckets[number];
}
std::cerr << "Total distincts numbers: " << nb_distincts << std::endl;
}
你可以看到它正在工作:
$ ./main | sort | uniq | wc -l
Total distincts numbers: 26
26
最简单的方法就是使用 std::set
。
std::set<int> s;
int uniqueCount = 0;
for( int i = 0; i < 30; ++i )
{
int n = get_number();
if( s.find(n) != s.end() ) {
--uniqueCount;
continue;
}
s.insert( n );
}
// now s contains unique numbers
// and uniqueCount contains the number of unique integers returned
使用array
和sort
似乎不错,但是如果您只需要计算不同的值,unique
可能有点矫枉过正。以下函数应返回排序范围内的非重复值数。
template<typename ForwardIterator>
size_t distinct(ForwardIterator begin, ForwardIterator end) {
if (begin == end) return 0;
size_t count = 1;
ForwardIterator prior = begin;
while (++begin != end)
{
if (*prior != *begin)
++count;
prior = begin;
}
return count;
}
与基于 set
或map
的方法相比,这种方法不需要任何堆分配,并且元素连续存储在内存中,因此它应该快得多。渐近时间复杂度O(N log N)
这与使用关联容器时相同。我敢打赌,即使是您使用std::sort
后跟std::unique
的原始解决方案也会比使用std::set
快得多。
尝试一个集合,尝试一个无序集合,尝试排序和唯一,尝试其他看起来有趣的事情。
然后测量每一个。如果你想要最快的实现,没有什么可以替代尝试真正的代码并看看它真正做了什么。
您的特定平台和编译器以及其他细节肯定很重要,因此请在尽可能接近它在生产环境中运行的环境中进行测试。
- 实现基于数字值(正、负、零)的条件表达式的最佳方法
- 将一系列数字映射到 CPP 中的值的简单方法
- 找到所有与自己求和的数字X的快速方法,去掉一个数字得到N
- 有没有更简单的方法可以从用户那里获取三个数字并按升序打印它们?
- 有效创建数字签名的正确方法是什么?我可以使用DSA_sign_setup()吗?
- 有什么方法可以在 c++ 中组织"cycling"(循环)数字?
- 查看数字是否包含在未排序的双数字间隔内的最有效方法?
- isdigit() 和 isalnum() 给出错误,因为输入是一个常量字符并且无法转换。其他可能查看输入是否为数字的方法?
- 有没有一种更快的方法,从特定的数字池中获取随机数
- sort() 方法 c++ 中的比较器函数.为大量数字获得不同的解决方案
- 在向量中找到连续数字的更有效方法
- 获取数字小数部分的最佳方法
- 获取数字最左边两个位的值的最便宜的方法是什么?
- 将两位数字转换为低内存表示的最快方法
- C/C++ 方法中最快/最短计算二进制数字总和/又名二进制中的 1 数
- 总结数字字符串输入的最佳方法
- 搜索字符串是否至少包含一次从 0 到 9 的所有数字的最有效方法
- 在C++有没有更好的方法可以做到这一点?检查哪些数字满足条件 [A*B*C = A! + B! + C!]
- C++:有哪些常规方法可以使代码更有效地用于大数字
- 对这些 n^2 个数字进行排序的最快方法是什么