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

发布于 2024-09-14 22:04:52 字数 1566 浏览 14 评论 0 原文

相关:我可以使用什么作为std::map键?

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

因此,我在 xyz Vector 上键入 std::map

class Vector
{ 
  float x,y,z
} ;

,并且我正在制作 std::map >。所以请注意这里的键不是一个std::vector,它是一个类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)<(-1,-1,1),和(-1,-1,1)>(-1,-1,-1)。

我的问题是这确实是人为的,虽然我已经对其进行了编码并且它有效,但我发现它用这种非常奇怪的、人为的、非基于数学的“小于”概念“污染”了我的 Vector 类(数学上)为一个向量。

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

建议?欢迎开箱即用的解决方案!

Related: what can I use as std::map keys?

I needed to create a mapping where specific key locations in space map to lists of objects. std::map seemed the way to do it.

So I'm keying a std::map on an xyz Vector

class Vector
{ 
  float x,y,z
} ;

, and I'm making a std::map<Vector, std::vector<Object*> >. So note the key here is not a std::vector, its an object of class Vector which is just a math xyz vector of my own making.

To produce a "strictly weak ordering" I've written the following overload for 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 ;
    }
  }

Its a bit long but basically makes it so any vector can consistently be said to be less than any other vector ((-1, -1, -1) < (-1,-1,1), and (-1, -1, 1) > (-1,-1,-1) for example).

My problem is this is really artificial and although I've coded it and it works, I am finding that it "pollutes" my Vector class (mathematically) with this really weird, artificial, non-math-based notion of "less than" for a vector.

But I need to create a mapping where specific key locations in space map to certain objects, and std::map seems the way to do it.

Suggestions? Out-of-box solutions welcome!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

初吻给了烟 2024-09-21 22:04:52

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

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

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

Instead of defining operator< for your key class, you can give the map a custom comparator. This is a function object that takes two arguments and returns true if the first comes before the second. Something like this:

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

typedef std::map<Vector, Value, CompareVectors> VectorValueMap;
哎呦我呸! 2024-09-21 22:04:52

您可以将其与班级分开。然后将其指定为 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;
}

You can separate it from the class. Then specify it as the comparison operator for the std::map.

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

Where Compare is a function (or functor) that can compare tow Vector objects.
I also think you can simplify your Compare operation.

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

Notice we test for less then greater and then fall through for equal. Personally I like the simplicity of this style. But I often see this being compressed like this:

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;
}
韵柒 2024-09-21 22:04:52

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

更简单的 mapset 的无序对应项为您提供了两个优点:

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

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

因此,只需继续使用:

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

这将使用默认的哈希函数来处理地图的插入/搜索。

PS:> > 事情将在即将推出的标准以及未来的编译器版本中得到修复。

I think std::tr1::unordered_map is just what you need. No strict weak ordering will be required. GCC has a something similar in tr1 namespace as well. Or go for Boost.Unordered.

The unordered counterparts of the more pedestrian map or set gives you two advantages:

  • You don't need to define a less-than operator where none makes sense

  • Hash tables may perform better than balanced binary trees, the latter being the preferred method of implementing the ordered map or set structures. But that depends on your data access pattern/requirements.

So, just go ahead and use:

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

This makes use of a default hash function that takes care of insertion/search for your map.

PS: the > > thingy will be fixed in the upcoming standard and hence future compiler versions.

不甘平庸 2024-09-21 22:04:52

你发现你的班级被这个污染了,这是正常的。从CS的角度来看,它也受到了污染。

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

然而,要问自己的第一个问题是:这是否有意义。问题是您为您的类定义了一个方法,该方法仅在有限的上下文中有意义,但在任何地方都可以访问。这就是为什么“污染”的感觉开始出现。

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

  • 我需要订购 Vector 吗?是:std::map,否:std::unordered_mapstd::tr1::unodered_mapstd::hash_map boost::unordered_map
  • 该集合是否拥有 Object ?是:boost::ptr_vectorstd::vector std::unique_ptr<对象>; >,否:std::vector

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

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

It's normal that you find that your class is polluted by this. It's also polluted from a CS point of view.

The normal way of defining such an operator is through (potentially friend) free functions.

However the first question to ask yourself is: does it makes sense. The issue is that you have defined a method for your class that is only meaningful in a limited context but accessible everywhere. That's why the "pollution" feeling kicks in.

Now, if I were to need such mapping from a Vector to a collection of Objects, here are the questions I would ask myself:

  • Do I need the Vector to be ordered ? Yes: std::map, No: std::unordered_map or std::tr1::unodered_map or std::hash_map or boost::unordered_map.
  • Will this collection owns the Object ? Yes: boost::ptr_vector<Object> or std::vector< std::unique_ptr<Object> >, No: std::vector<Object*>

Now, in both cases (map and unordered_map), I will need something to transform my key. The collection provide a supplementary template argument which takes a Functor type.

Beware: as has been mentioned in another answer, floating point representation is awkward in a computer, therefore you will probably need to relax the meaning of equality and ignore the lower order digits (how many depends on your computations).

野鹿林 2024-09-21 22:04:52

我认为你的方法很好。如果您担心污染 Vector 类,那么我相信独立函数也能正常工作:

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

但请注意:您在这里所做的事情相当危险。浮点计算经常会出现错误。假设您在地图中插入一些点。然后计算一个点并检查地图以查看它是否在那里。即使从严格的数学角度来看,第二个点与第一个点相同,也不能保证您会在地图中找到它。

I think your approach is fine. If you're worried about polluting the Vector class, then I believe a stand-alone function will work just as well:

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

But just a word of warning: what you're doing here is pretty risky. There are often errors in floating point calculations. Suppose you insert some point into your map. Then you calculate a point and check the map to see if it's there. Even if, from a strictly mathematical point of view, the second point is the same as the first, there's no guarantee that you'll find it in the map.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文