C++ 计算不同数字数量的高效时间方法

C++ 计算不同数字数量的高效时间方法,c++,c++11,C++,C++11,get\u number()返回一个整数。我将调用它30次,然后计算返回的不同整数的数量。我的计划是将这些数字放入std::array,对其进行排序,然后使用std::unique 这是一个好的解决方案吗?有更好的吗?这段代码将成为我程序的瓶颈 我认为应该有一个基于散列的解决方案,但当我只有30个元素时,它的开销可能会太大 编辑我将“唯一”更改为“不同”。例如: {1,1,1,1} => 1 {1,2,3,4} => 4 {1,3,3,1} => 2 最简单的方法就是使用st

get\u number()
返回一个整数。我将调用它30次,然后计算返回的不同整数的数量。我的计划是将这些数字放入
std::array
,对其进行排序,然后使用
std::unique

这是一个好的解决方案吗?有更好的吗?这段代码将成为我程序的瓶颈

我认为应该有一个基于散列的解决方案,但当我只有30个元素时,它的开销可能会太大

编辑我将“唯一”更改为“不同”。例如:

{1,1,1,1} => 1
{1,2,3,4} => 4
{1,3,3,1} => 2

最简单的方法就是使用
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
std::set s;
int uniqueCount=0;
对于(int i=0;i<30;++i)
{
int n=get_number();
如果(s.find(n)!=s.end()){
--唯一计数;
继续;
}
s、 插入(n);
}
//现在s包含唯一的数字
//uniqueCount包含返回的唯一整数数

我会使用
std::set
,因为它更简单:

std::set<int> s;
for(/*loop 30 times*/)
{
   s.insert(get_number());
}
std::cout << s.size() << std::endl; // You get count of unique numbers

最简单的解决方案是使用
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::映射计数器;
对于(尺寸i=0;i!=30;++i){
计数器[getNumber()]+=1;
}
std::向量唯一性;
用于(自动常数和配对:计数器){
如果(pair.second==1){uniques.push_back(pair.first);}
}
//uniques现在包含只出现一次的项。

使用
数组
排序
似乎不错,但如果只需要计算不同的值,则
唯一
可能有点过分。以下函数应返回排序范围内不同值的数目

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;
}
模板
大小不同(ForwardIterator开始,ForwardIterator结束){
if(begin==end)返回0;
大小\u t计数=1;
ForwardIterator Previor=开始;
while(++开始!=结束)
{
如果(*之前!=*开始)
++计数;
优先=开始;
}
返回计数;
}

与基于
set
map
的方法相比,这种方法不需要任何堆分配,元素连续存储在内存中,因此速度应该快得多。渐近时间复杂度为
O(N logn)
,这与使用关联容器时相同。我敢打赌,即使您最初使用
std::sort
然后再使用
std::unique
的解决方案,也会比使用
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
然后测量每一个。如果您想要最快的实现,那么除了尝试真正的代码并了解它真正的功能外,没有其他方法可以替代


您的特定平台、编译器和其他细节肯定会有影响,因此在尽可能接近其将在生产中运行的环境中进行测试。

使用
std::map
std::set
std::sort
算法将为您提供
O(n*log(n))
复杂性。对于小到大数量的元素,它是完全正确的但您使用的是一个已知的整数范围,这为许多优化打开了大门。

正如您(在评论中)所说,整数的范围是已知的且很短的:
[0..99]
。我建议实现一种改进的计数排序。见:

您可以在自行排序时计算不同项目的数量,从而无需调用
std::unique
。整个复杂性将是
O(n)
。另一个优点是所需的内存与输入项的数量无关。如果有30.000.000.000个整数要排序,则不需要单个补充字节来计算不同的项

即使允许的整数值的范围很大,也表示
[0..10.000.000]
消耗的内存将非常低。事实上,一个优化的版本每允许一个整数值可以消耗低至1位。这是少于2MB内存或笔记本电脑ram的千分之一

下面是一个简短的示例程序:

#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;
}

最简单的解决方案:将它们粘贴在
集合中
。你知道数字的范围吗(例如,它们将在
[0255]
中)?@BoBTFish:是的,它们会。称30次将是瓶颈?@Andreas“是的,它们会”是什么意思?它们将在某个已知范围内(告诉我们它是什么)?它们将在我凭空抽出的特定范围内?@BoBTFish是的,这是一个很好的猜测。事实上[0,99],但我想这并不重要。我不知道怎么做,除非你打算使用两套?这没有帮助,因为事实上我对这个问题感到困惑(可能是因为我不是母语人士),所以我不知道OP希望计算多少。我最初的想法是,他想知道出现过一次的项目的数量,但显然其他人都认为他想知道出现过的不同项目的数量。@MatthieuM。OP提出的算法是计算不同项目的数量,所以这很可能是他想要做的。@MatthieuM,也许你是对的,我们应该回答要求我们回答的问题。再次更新了我的答案。@IvanGrynko:有时候很难理解被问到的问题:(顺便说一句,你可以使用
insert
返回
pair
,这样
if(不是s.insert(n)。第二){--uniqueCount;}
就可以替换大部分身体循环(可能不太清晰).1考虑到OP问题的参数,这似乎是唯一符合要求的可行答案。OP最好提出一个问题:“我如何找到只出现一次的不同元素?”尽管这是公认的答案(而且确实是一个好答案),看看下面fjardon的方法。在这种情况下,树的开销(内部使用
std::map
std::set
可能会导致比最简单的C风格解决方案更差的性能。
$ ./main | sort | uniq | wc -l
Total distincts numbers: 26
26