【问题标题】:Has this vector occured before这个向量之前是否出现过
【发布时间】:2017-08-15 02:12:54
【问题描述】:

我有很多向量(大约 10^4,甚至更多!)而且我将从流中获得更多的输入向量。所以,例如,我有

  • v1 = 1 0 4 1 1
  • v2 = 1 1 2 5 3 6 2
  • v3 = 0 1 1 5 0

我有 10^4 个这样的向量 现在,我输入了一个向量v4 = 0 1 1 5 0,我想检查它之前是否出现过,你建议我怎么做?

我将列出我想到的技术,以及伴随它们的错误:

  • 使用std::mapstd::set 相同。但是,std::map std::set 不支持向量作为参数。
  • 要将向量中的每个整数转换为字符串类型,请按顺序附加它们并将字符串存储在映射中。错误:v5 = 11 1 1 1v6 = 1 1 1 1 1 的大小写将显示为相同。
  • 与上面类似,只是在每个整数后添加一个分隔符。错误:编码太乏味?

我想知道您是否可以想出一些方法来实现这一点?

编辑: 对于 10^4,这是可以实现的。我的新任务要求我最多存储 10^9。我个人认为 STL 没有那么大的空间,他们抛出了SIGABRT 错误。您知道在这种情况下可以使用的任何其他有效散列方法吗?

【问题讨论】:

  • 将向量转换为逗号分隔的字符串应该不会很繁琐。这似乎是解决这个问题的最简单方法。
  • 我认为散列函数会有所帮助
  • 你考虑过Bloom filter吗?
  • 将向量存储在 trie 结构中。然后你就可以非常轻松高效地检查它了
  • @hiteshn97 是什么让您认为std::set<std::vector<int>> 不是有效类型?

标签: c++ vector stl implementation


【解决方案1】:

执行此操作的简单方法是将向量存储在另一个向量中,并使用 std::sort() 系列函数保持其顺序,使用 std::lexigraphical_compare 作为排序谓词。这将允许在 O(log(n)) 摊销时间内对列表进行二进制搜索,代价是半成本的排序操作,这可能可以通过玩一些游戏来减少向量列表的堆积或分区为你加载它。

然而,比这更有效的是将向量存储为特里树 (https://en.wikipedia.org/wiki/Trie),其中沿着特里树的每条路径都存储来自向量的唯一序列。根据数据的差异,这可以更节省空间,并且加法和搜索都是 O(log(n)) 操作。

请接受我的建议,但是,10^4 个元素实际上是一个很小的数字。我的经验是,当您的数据集处于 10^6-10^7 范围内时,效率排序和搜索算法的差异才真正开始在现代硬件上表现出来。低于这个规模,通常最简单、最适合缓存的算法会胜出。

如果您只是追求原始速度,并且要扫描的向量列表是众所周知且静态的,那么另一种选择是使用有限状态机来接受/拒绝您的输入。 Ragel 之类的工具可以轻松解决此类问题。

【讨论】:

  • 第一种方法。我没明白。我能看出你正在对向量进行排序,但是你的算法能够区分 {1,2,3} 和 {3,2,1} 吗?
【解决方案2】:

这是非常初学者的方法,但我正在尝试使用我从折叠和 stl 中学到的东西

方法说明:

1.创建了一个向量列表(用于输入目的,无论如何都可以)

2.保留一个主向量v,它将存储主折叠向量

3.used stl 包括在折叠前继续检查序列是否存在

输入集

std::vector<int> x ={1,2,3};
std::vector<int> y ={7,8,9};
std::vector<int> z ={1,2,3};
std::vector<int> a ={1,2,3};  
std::vector<int> v5 = {11,1,1,1}; //as mentioned in question
std::vector<int> v6 = {1,1,1,1};  //as mentioned in question

方法

#include <iostream>
#include <vector>
#include <algorithm>
#include <list>

template <typename T>
void Concat(std::vector<T>& v, const std::vector<T>& v2)
{
    v.insert(v.end(), v2.begin(), v2.end());
}

template <typename T>
void Concat(std::vector<T>& v, const T& value)
{
    v.push_back(value);
}

template<typename T, typename... Args>
void push_back_vec(std::vector<T>& v, Args&&... args)
{
    (Concat(v, args), ...);
}
int main()
{
    std::vector<int> v;
    std::list<std::vector<int> > m ;
    std::vector<int> x ={1,2,3};
    std::vector<int> y ={7,8,9};
    std::vector<int> z ={1,2,3};
    std::vector<int> a ={1,2,3};
    std::vector<int> v5 = {11,1,1,1};
    std::vector<int> v6 = {1,1,1,1};
    m.push_back(x);
    m.push_back(y);
    m.push_back(z);
    m.push_back(a);
    m.push_back(v5);
    m.push_back(v6);

    for (std::list<std::vector<int> >::iterator it1 = m.begin(); it1 != m.end(); ++it1)
    {


        if (std::includes(v.begin(), v.end(), (*it1).begin(), (*it1).end()))
        {
            std::cout<<"Already present"<<std::endl;
            }
        else
            {
            push_back_vec(v,(*it1));

            }
    }

    for (int i : v) std::cout << i << ' ';

}

输出

Already present
Already present
1 2 3 7 8 9 11 1 1 1 1 1 1 1 Program ended with exit code: 0

我知道可以有很多改进,但在某些极端情况下可能会失败。这只是尝试之一,欢迎批评并帮助我改进

【讨论】:

  • 我对向量的折叠了解不多。我尝试用谷歌搜索它,但没有找到任何有用的资源。你能给我一个链接吗?你的算法的时间复杂度是多少。 O(n) 太大了!
【解决方案3】:

如果您在向量上定义完整的排序,您可以通过两种方式进行相当有效的查找:

  • 将现有向量存储在std::setstd::map 中。这些是有序的容器类,具有相当有效的成员资格/查找方法。
  • 将现有向量按排序顺序存储在std::vector 中,并使用std::binary_search

向量排序的默认选择是字典顺序。这是由std::vector 实现提供的operator&lt; 提供的;它的实际作用是这样的:

bool operator<(const std::vector<int> &a, const std::vector<int> &b) {
  auto a_it = a.cbegin();
  auto b_it = b.cbegin();
  while(a_it < a.cend() && b_it < b.cend()) {
    if(*a_it < *b_it) {
      return true;
    }
    if(*b_it < *a_it) {
      return false;
    }
    ++a_it;
    ++b_it;
  }
  if(a_it == a.cend() && b_it < b.cend()) {
    return true;
  }
  return false;
}

请注意,此代码可以提前退出:如果输入向量的第一个元素不同,则无需进一步检查。只有当有一个长的公共前缀(或者向量实际上相同)时,它才需要检查所有元素。


如 cmets 中所述,您还可以通过以下方式解决此问题:

  • 散列映射 (std::unordered_map) -- 要求您为 std::vector&lt;int&gt; 定义散列
  • trie -- AFAIK 没有 std:: 实现,您需要找到一个库或滚动自己的库

【讨论】:

  • 那里 already exists namespace std { template&lt; class T, class Alloc &gt; bool operator&lt;( const vector&lt;T,Alloc&gt;&amp; lhs, const vector&lt;T,Alloc&gt;&amp; rhs ); }std::less&lt;std::vector&lt;int&gt;&gt; 的默认值工作正常
  • 我注意到您使用 a.cbegin() 而不是 a.begin() ,有什么具体原因吗?还是只是个人选择?可能是为了确保您不更改矢量的内容?可能是因为您通过引用传递。这样对吗?为什么在这种情况下返回 true? if(*a_it &lt; *b_it) return true;
  • 按字母顺序考虑:要确定是否为"cat" &lt; "dog",首先检查是否为'c' &lt; 'd'。如果是这样,您知道"cat" 是第一位的——您不需要检查任何其他字符,您知道结果是true
  • 另外,我用cbegin()强调输入是不变的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
相关资源
最近更新 更多