在std :: map中使用(数学)向量

时间:2010-08-18 18:27:10

标签: c++ math operator-overloading stdmap

相关:what can I use as std::map keys?

我需要创建一个映射,其中空间中的特定键位置映射到对象列表。 std::map似乎是这样做的。

所以我在xyz std::map

上键入Vector
class Vector
{ 
  float x,y,z
} ;

,我正在制作std::map<Vector, std::vector<Object*> >。所以注意这里的关键不是一个std::vector,它是class Vector的一个对象,它只是我自己制作的数学xyz向量。

为了产生“严格弱的排序”,我为operator<编写了以下重载:

  bool Vector::operator<( const Vector & b ) const {
    // z trumps, then y, then x
    if( z < b.z )
    {
      return true ;
    }
    else if( z == b.z )
    {
      if( y < b.y )
      {
        // z == b.z and y < b.y
        return true ;
      }
      else if( y == b.y )
      {
        if( x < b.x )
        {
          return true ;
        }
        else if( x == b.x )
        {
          // completely equal
          return false ;
        }
        else
        {
          return false ;
        }
      }
      else
      {
        // z==b.z and y >= b.y
        return false ;
      }
    }
    else
    {
      // z >= b.z
      return false ;
    }
  }

它有点长,但基本上是这样的,所以任何矢量都可以始终被认为小于任何其他矢量((-1,-1,-1)&lt;(-1,-1,1),和( -1,-1,1)&gt;(-1,-1,-1)例如)。

我的问题是这真的是人为的,虽然我已编码并且它有效但我发现它“污染”我的Vector类(数学上)这个非常奇怪,人为的,非基于数学的概念“小于“对于矢量。

但我需要创建一个映射,其中空间中的特定键位置映射到某些对象,而std :: map似乎是这样做的。

连连呢?开箱即用的解决方案欢迎!!

5 个答案:

答案 0 :(得分:5)

您可以为地图提供自定义比较器,而不是为您的键类定义operator<。这是一个函数对象,它接受两个参数,如果第一个参数位于第二个参数之前,则返回true。像这样:

struct CompareVectors
{
    bool operator()(const Vector& a, const Vector& b)
    {
        // insert comparison code from question
    }
};

typedef std::map<Vector, Value, CompareVectors> VectorValueMap;

答案 1 :(得分:3)

您可以将它与班级分开。然后将其指定为std :: map的比较运算符。

std::map<Vector,std::vector<Object*>,Compare>  data;

其中Compare是一个可以比较两个Vector对象的函数(或函子) 我还认为你可以简化比较操作。

bool Compare<( const Vector& lhs, const Vector& rhs)
{
   // z trumps, then y, then x
   if( lhs.z < rhs.z )
   {    return true ;
   }
   else if (lhs.z > rhs.z)
   {    return false;
   }
   // Otherwise z is equal
   if( lhs.y < rhs.y )
   {    return true ;
   }
   else if( lhs.y > rhs.y )
   {    return false;
   }
   // Otherwise z and y are equal
   if ( lhs.x < rhs.x )
   {    return true;
   }
   /* Simple optimization Do not need this test
      If this fails or succeeded the result is false.
   else if( lhs.x > rhs.x )
   {    return false;
   }*/
   // Otherwise z and y and x are all equal
   return false;
}

请注意,我们测试的次数越少越好,然后等于下降。就个人而言,我喜欢这种风格的简约。但我经常看到这样被压缩:

bool Compare<( const Vector& lhs, const Vector& rhs)
{
    // Note I use three separate if statements here for clarity.
    // Combining them into a single statement is trivial/
    //
    if ((lhs.z < rhs.z)                                        ) {return true;}
    if ((lhs.z == rhs.z) && (lhs.y < rhs.y)                    ) {return true;}
    if ((lhs.z == rhs.z) && (lhs.y == rhs.y) && (lhs.x < rhs.x)) {return true;}

    return false;
}

答案 2 :(得分:1)

我认为std::tr1::unordered_map正是您所需要的。不需要严格的弱排序。 GCC在tr1命名空间中也有类似的东西。或者去Boost.Unordered

行人mapset的无序对手为您带来两个好处:

  • 您无需定义一个没有意义的小于运算符

  • 散列表可能比平衡二叉树表现更好,后者是实现有序mapset结构的首选方法。但这取决于您的数据访问模式/要求。

所以,请继续使用:

typedef std::tr1::unordered_map<Vector, std::vector<Object *> > VectorMap;

这使用了一个默认的哈希函数,负责插入/搜索你的地图。

PS:> >内容将在即将推出的标准中修复,因此将来的编译器版本将被修复。

答案 3 :(得分:1)

您发现您的课程受此污染是正常的。从CS的角度来看,它也受到了污染。

定义此类运算符的常规方法是通过(可能是朋友)自由函数。

然而,问自己的第一个问题是:它是否有意义。问题是您已经为您的类定义了一个方法,该方法仅在有限的上下文中有意义,但在任何地方都可访问。这就是“污染”感觉的原因。

现在,如果我需要从VectorObject的集合进行此类映射,以下是我会问自己的问题:

  • 我是否需要订购Vector?是的:std::map,否:std::unordered_mapstd::tr1::unodered_mapstd::hash_mapboost::unordered_map
  • 此系列是否拥有Object?是的:boost::ptr_vector<Object>std::vector< std::unique_ptr<Object> >,否:std::vector<Object*>

现在,在这两种情况下(mapunordered_map),我都需要一些东西来改变我的密钥。该集合提供了一个补充模板参数,该参数采用Functor类型。

注意:正如在另一个答案中提到的,浮点表示在计算机中很笨拙,因此您可能需要放松相等的含义并忽略低位数(多少取决于您的计算)。

答案 4 :(得分:0)

我认为你的方法很好。如果您担心污染Vector类,那么我相信一个独立的功能也会起作用:

bool operator<( const Vector& lhs, const Vector& rhs )
{
    ...
}

但只是一句警告:你在这里做的事情风险很大。浮点计算经常出错。假设您在地图中插入了一些点。然后你计算一个点并检查地图,看它是否在那里。即使从严格的数学观点来看,第二点与第一点相同,也无法保证你会在地图中找到它。