使用正确的优化算法C ++查找3D空间中的点之间的距离

时间:2015-07-17 21:35:27

标签: c++ math optimization vector 3d

使用数学公式可以很容易地找到3D空间中任意两点之间的距离:isArray:true

计算空间中任意两点之间的距离非常容易,每个点在3D空间中都有(x,y z)坐标。

我的问题,将这个公式扩展到我正面临优化打嗝的大规模。我所做的是创建一个简单的√((x2-x1)^2 )+(y2-y1)^2+(z2-z1)^2类,并使用这个用C ++编码的公式来计算距离。

如果有1000分怎么办?我该怎么办呢?我在想使用简单的Vector来存储这些3D点并不是优化计算这1000个点之间距离的方法。

2 个答案:

答案 0 :(得分:4)

有不同程度的优化,每种都取决于实际情况。

在实施级别,您希望以友好的方式将数据布局到处理器。 即

  1. 连续
  2. 正确对齐
  3. vector(SSE)友好(如果您使用垂直SSE(通常),或者AoS用于水平处理,则为SoA)
  4. 实际启用矢量化(编译器选项或手工艺)
  5. 这可能会使你获得高达10%的提升。

    在算法级别,您可能想再次考虑为什么需要核心循环中每个点的距离

    1. 你是否需要经常重新计算(需要在...中) 模拟)或者您可以使用陈旧的值并可能重新计算 在背景中(例如在游戏中)
    2. 您可以使用距离平方,可以保存一个sqrt吗?
    3. 如果您的数据集不经常更改,如何将其转换为极坐标,您可以缓存每个点的r ^ 2, 距离平方公式然后减少到大约一个cos()操作: r1 ^ 2 + r2 ^ 2 - 2r1r2 cos(a1-a2)。
    4. 编辑:3D空间中的Opps复杂度与笛卡尔坐标相同,忽略第3点。

      GPU考虑因素

      虽然1000点太少而无法进行交易,但如果你将来有更多的积分,你仍然可以考虑将工作卸载到GPU上。

答案 1 :(得分:0)

这是一个可以帮助你的Vector3类对象的版本。

Vector3.h

#ifndef VECTOR3_H
#define VECTOR3_H

#include "stdafx.h"
#include "GeneralMath.h"

class Vector3 { 

public:
    union {
        float m_f3[3];
        struct {
            float m_fX;
            float m_fY;
            float m_fZ;
        };  
    };

    inline Vector3();
    inline Vector3( float x, float y, float z );
    inline Vector3( float *pfv );
    ~Vector3();

    // Operators
    inline Vector3  operator+( const Vector3 &v3 ) const;
    inline Vector3  operator+() const;
    inline Vector3& operator+=( const Vector3 &v3 );
    inline Vector3  operator-( const Vector3 &v3 ) const;
    inline Vector3  operator-() const;
    inline Vector3& operator-=( const Vector3 &v3 );
    inline Vector3  operator*( const float &fValue ) const;
    inline Vector3& operator*=( const float &fValue );
    inline Vector3  operator/( const float &fValue ) const;
    inline Vector3& operator/=( const float &fValue );

    // -------------------------------------------------------------------
    // operator*()
    // Pre Multiple Vector By A Scalar
    inline friend Vector3 Vector3::operator*( const float &fValue, const Vector3 v3 ) {

        return Vector3( fValue*v3.m_fX, fValue*v3.m_fY, fValue*v3.m_fZ );

    } // operator*

    // -------------------------------------------------------------------
    // operator/()
    // Pre Divide Vector By A Scalar Value
    inline friend Vector3 Vector3::operator/( const float &fValue, const Vector3 v3 ) {
        Vector3 vec3;
        if ( Math::isZero( v3.m_fX ) ) {
            vec3.m_fX = 0.0f;
        } else {
            vec3.m_fX = fValue / v3.m_fX;
        }

        if ( Math::isZero( v3.m_fY ) ) {
            vec3.m_fY = 0.0f;
        } else {
            vec3.m_fY = fValue / v3.m_fY;
        }

        if ( Math::isZero( v3.m_fZ ) ) {
            vec3.m_fZ = 0.0f;
        } else {
            vec3.m_fZ = fValue / v3.m_fZ;
        }

        return vec3;
    } // operator/

    // Functions    
    inline Vector3 rotateX( float fRadians );
    inline Vector3 rotateY( float fRadians );
    inline Vector3 rotateZ( float fRadians );

    inline void setPerpendicularXZ( Vector3 v3 );
    inline void setPerpendicularXY( Vector3 v3 );
    inline void setPerpendicularYZ( Vector3 v3 );

    inline Vector3  cross( const Vector3 v3 ) const;
    inline float    dot( const Vector3 v3 ) const;
    inline float    getAngle( const Vector3 &v3, const bool bNormalized = false, bool bRadians = true );
    inline float    getCosAngle( const Vector3 &v3, const bool bNormalized = false );
    inline float    length() const;
    inline float    length2() const;
    inline void     normalize();
    inline void     zero();
    inline bool     isZero() const;

}; // Vector3

// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3() {
    m_fX = 0.0f;
    m_fY = 0.0f;
    m_fZ = 0.0f;
} // Vector3

// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float x, float y, float z ) {
    m_fX = x;
    m_fY = y;
    m_fZ = z;
} // Vector3

// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float *pfv ) {
    m_fX = pfv[0];
    m_fY = pfv[1];
    m_fZ = pfv[2];
} // Vector3

// -----------------------------------------------------------------------
// operator+()
// Unary - Operator:
inline Vector3 Vector3::operator+() const {
    return *this;
} // operator+

// -----------------------------------------------------------------------
// operator+()
// Binary - Add Two Vectors Together
inline Vector3 Vector3::operator+( const Vector3 &v3 ) const {
    return Vector3( m_fX + v3.m_fX, m_fY + v3.m_fY, m_fZ + v3.m_fZ );
} // operator+

// -----------------------------------------------------------------------
// operator+=()
// Add Two Vectors Together
inline Vector3 &Vector3::operator+=( const Vector3 &v3 ) {
    m_fX += v3.m_fX;
    m_fY += v3.m_fY;
    m_fZ += v3.m_fZ;
    return *this;
} // operator+=

// -----------------------------------------------------------------------
// operator-()
// Unary - Operator: Negate Each Value
inline Vector3 Vector3::operator-() const {
    return Vector3( -m_fX, -m_fY, -m_fZ );
} // operator-

// -----------------------------------------------------------------------
// operator-()
// Binary - Take This Vector And Subtract Another Vector From It
inline Vector3 Vector3::operator-( const Vector3 &v3 ) const {
    return Vector3( m_fX - v3.m_fX, m_fY - v3.m_fY, m_fZ -v3.m_fZ );
} // operator-

// -----------------------------------------------------------------------
// operator-=()
// Subtract Two Vectors From Each Other
inline Vector3 &Vector3::operator-=( const Vector3 &v3 ) {
    m_fX -= v3.m_fX;
    m_fY -= v3.m_fY;
    m_fZ -= v3.m_fZ;
    return *this;
} // operator-=

// -----------------------------------------------------------------------
// operator*()
// Post Multiply Vector By A Scalar
inline Vector3 Vector3::operator*( const float &fValue ) const {
    return Vector3( m_fX*fValue, m_fY*fValue, m_fZ*fValue );
} // operator*

// -----------------------------------------------------------------------
// operator*=()
// Multiply This Vector By A Scalar
inline Vector3& Vector3::operator*=( const float &fValue ) {
    m_fX *= fValue;
    m_fY *= fValue;
    m_fZ *= fValue;
    return *this;
} // operator*=

// -----------------------------------------------------------------------
// operator/()
// Post Divide Vector By A Scalar
inline Vector3 Vector3::operator/( const float &fValue ) const {
    Vector3 v3;
    if ( Math::IsZero( fValue ) ) {
        v3.m_fX = 0.0f;
        v3.m_fY = 0.0f;
        v3.m_fZ = 0.0f;
    } else {
        float fValue_Inv = 1/fValue;
        v3.m_fX = v3.m_fX * fValue_Inv;
        v3.m_fY = v3.m_fY * fValue_Inv;
        v3.m_fZ = v3.m_fZ * fValue_Inv;
    }
    return v3;
} // operator/

// -----------------------------------------------------------------------
// operator/=()
// Divide This Vector By A Scalar
inline Vector3& Vector3::operator/=( const float &fValue ) {
    if ( Math::isZero( fValue ) ) {
        m_fX = 0.0f;
        m_fY = 0.0f;
        m_fZ = 0.0f;
    } else {
        float fValue_Inv = 1/fValue;
        m_fX *= fValue_Inv;
        m_fY *= fValue_Inv;
        m_fZ *= fValue_Inv;
    }
    return *this;
} // operator/=

// -----------------------------------------------------------------------
// rotateX()
// Rotate This Vector About The X Axis
inline Vector3 Vector3::rotateX( float fRadians ) {
    Vector3 v3;
    v3.m_fX =  m_fX;
    v3.m_fY =  m_fY * cos( fRadians ) - m_fZ * sin( fRadians );
    v3.m_fZ =  m_fY * sin( fRadians ) + m_fZ * cos( fRadians );
    return v3;
} // rotateX

// -----------------------------------------------------------------------
// rotateY()
// Rotate This Vector About The Y Axis
inline Vector3 Vector3::rotateY( float fRadians ) {
    Vector3 v3;
    v3.m_fX =  m_fX * cos( fRadians ) + m_fZ * sin( fRadians );
    v3.m_fY =  m_fY;
    v3.m_fZ = -m_fX * sin( fRadians ) + m_fZ * cos( fRadians );
    return v3;
} // rotateY

// -----------------------------------------------------------------------
// rotateZ()
// Rotate This Vector About The Z Axis
inline Vector3 Vector3::rotateZ( float fRadians ) {
    Vector3 v3;
    v3.m_fX = m_fX * cos( fRadians ) - m_fY * sin( fRadians );
    v3.m_fY = m_fX * sin( fRadians ) + m_fY * cos( fRadians );
    v3.m_fZ = m_fZ;
    return v3;
} // rotateZ

// -----------------------------------------------------------------------
// setPerpendicularXY()
// Make This Vector Perpendicular To Vector3
inline void Vector3::setPerpendicularXY( Vector3 v3 ) {
    m_fX = -v3.m_fY;
    m_fY =  v3.m_fX;
    m_fZ =  v3.m_fZ;
} // setPerpendicularXY

// -----------------------------------------------------------------------
// setPerpendicularXZ()
// Make This Vector Perpendicular To Vector3
inline void Vector3::setPerpendicularXZ( Vector3 v3 ) {
    m_fX = -v3.m_fZ;
    m_fY =  v3.m_fY;
    m_fZ =  v3.m_fX;
} // setPerpendicularXZ

// -----------------------------------------------------------------------
// setPerpendicularYZ()
// Make This Vector Perpendicular To Vector3
inline void Vector3::setPerpendicularYZ( Vector3 v3 ) {
    m_fX =  v3.m_fX;
    m_fY = -v3.m_fZ;
    m_fZ =  v3.m_fY;
} // setPerpendicularYZ

// -----------------------------------------------------------------------
// cross()
// Get The Cross Product Of Two Vectors
inline Vector3 Vector3::cross( const Vector3 v3 ) const {
    return Vector3( m_fY*v3.m_fZ - m_fZ*v3.m_fY,
                    v3.m_fX*m_fZ - m_fX*v3.m_fZ,
                    m_fX*v3.m_fY - m_fY*v3.m_fX );
} // cross

// -----------------------------------------------------------------------
// dot()
// Return The Dot Product Between This Vector And Another One
inline float Vector3::dot( const Vector3 v3 ) const {
    return ( m_fX * v3.m_fX + 
             m_fY * v3.m_fY +
             m_fZ * v3.m_fZ );
} // dot

// -----------------------------------------------------------------------
// normalize()
// Make The Length Of This Vector Equal To One
inline void Vector3::normalize() {
    float fMag;
    fMag = sqrt( m_fX*m_fX + m_fY*m_fY + m_fZ*m_fZ );
    if ( fMag <= Math::ZERO ) {
        m_fX = 0.0f;
        m_fY = 0.0f;
        m_fZ = 0.0f;
        return;
    }

    fMag = 1/fMag;
    m_fX *= fMag;
    m_fY *= fMag;
    m_fZ *= fMag;
} // normalize

// -----------------------------------------------------------------------
// isZero()
// Return True if Vector Is (0,0,0)
inline bool Vector3::isZero() const {
    if ( Math::isZero( m_fX ) && 
         Math::isZero( m_fY ) && 
         Math::isZero( m_fZ ) ) {
        return true;
    } else {
        return false;
    }
} // isZero

// -----------------------------------------------------------------------
// zero()
// Set The Vector To (0,0,0)
inline void Vector3::zero() {
    m_fX = 0.0f;
    m_fY = 0.0f;
    m_fZ = 0.0f;
} // zero

// -----------------------------------------------------------------------
// getCosAngle()
// Returns The cos(Angle) Value Between This Vector And Vector V. This
// Is Less Expensive Than Using GetAngle
inline float Vector3::getCosAngle( const Vector3 &v3, const bool bNormalized ) {
    // a . b = |a||b|cos(angle)
    // -> cos-1((a.b)/(|a||b|))

    // Make Sure We Do Not Divide By Zero
    float fMagA = length();
    if ( fMagA <= Math::ZERO ) {
        // This (A) Is An Invalid Vector
        return 0;
    }

    float fValue = 0;
    if ( bNormalized ) {
        // v3 Is Already Normalized
        fValue = dot(v3)/fMagA;
    } else {
        float fMagB = v3.length();
        if ( fMagB <= Math::ZERO) {
             // B Is An Invalid Vector
             return 0;
        }
        fValue = dot(v3)/(fMagA*fMagB);
    }

    // Correct Value Due To Rounding Problem
    Math::constrain( -1.0f, 1.0f, fValue );

    return fValue;
} // getCosAngle

// -----------------------------------------------------------------------
// getAngle()
// Returns The Angle Between This Vector And Vector V in Radians.
// This Is More Expensive Than Using getCosAngle
inline float Vector3::getAngle( const Vector3 &v3, const bool bNormalized, bool bRadians ) {
    // a . b = |a||b|cos(angle)
    // -> cos-1((a.b)/(|a||b|))

    if ( bRadians ) {
        return acos( this->getCosAngle( v3 ) );
    } else {
        // Convert To Degrees
        return Math::radian2Degree( acos( getCosAngle( v3, bNormalized ) ) );
    }
} // getAngle

// -----------------------------------------------------------------------
// length()
// Return The Absolute Length Of This Vector
inline float Vector3::length() const {
    return sqrtf( m_fX * m_fX + 
                  m_fY * m_fY +
                  m_fZ * m_fZ );
 } // length

 // -----------------------------------------------------------------------
 // length2()
 // Return The Relative Length Of This Vector - Doesn't Use sqrt() Less Expensive
 inline float Vector3::length2() const {
     return ( m_fX * m_fX +
              m_fY * m_fY +
              m_fZ * m_fZ );
 } // length2

#endif // VECTOR3_H

Vector3.cpp

#include "Vector3.h"

GeneralMath.h

#ifndef GENERALMATH_H
#define GENERALMATH_H

#include "stdafx.h"

class Math {
public:
    static const float PI;
    static const float PI_HALVES;
    static const float PI_THIRDS;
    static const float PI_FOURTHS;
    static const float PI_SIXTHS;
    static const float PI_2;
    static const float PI_INVx180;
    static const float PI_DIV180;
    static const float PI_INV;
    static const float ZERO;

    Math();

    inline static bool  isZero( float fValue );
    inline static float sign( float fValue );

    inline static int   randomRange( int iMin, int iMax );
    inline static float randomRange( float fMin, float fMax );

    inline static float degree2Radian( float fDegrees );
    inline static float radian2Degree( float fRadians );
    inline static float correctAngle( float fAngle, bool bDegrees, float fAngleStart = 0.0f );
    inline static float mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX );

    template<class T>
    inline static void constrain( T min, T max, T &value );

    template<class T>
    inline static void swap( T &value1, T &value2 );

}; // Math

// -----------------------------------------------------------------------
// degree2Radian()
// Convert Angle In Degrees To Radians
inline float Math::degree2Radian( float fDegrees ) {
    return fDegrees * PI_DIV180;
} // degree2Radian

// -----------------------------------------------------------------------
// radian2Degree()
// Convert Angle In Radians To Degrees
inline float Math::radian2Degree( float fRadians ) {
    return fRadians * PI_INVx180;
} // radian2Degree

// -----------------------------------------------------------------------
// correctAngle()
// Returns An Angle Value That Is Alway Between fAngleStart And fAngleStart + 360
// If Radians Are Used, Then Range Is fAngleStart To fAngleStart + 2PI
inline float Math::correctAngle( float fAngle, bool bDegrees, float fAngleStart ) {
    if ( bDegrees ) {
        // Using Degrees
        if ( fAngle < fAngleStart ) {
            while ( fAngle < fAngleStart ) {
                fAngle += 360.0f;
            }
        } else if ( fAngle >= (fAngleStart + 360.0f) ) {
            while ( fAngle >= (fAngleStart + 360.0f) ) {
                fAngle -= 360.0f;
            }
        }
        return fAngle;
    } else {
        // Using Radians
        if ( fAngle < fAngleStart ) {
            while ( fAngle < fAngleStart ) {
                fAngle += Math::PI_2;
            }
        } else if ( fAngle >= (fAngleStart + Math::PI_2) ) {
            while ( fAngle >= (fAngleStart + Math::PI_2) ) {
                fAngle -= Math::PI_2;
            }
        }
        return fAngle;
    }
} // correctAngle

// -----------------------------------------------------------------------
// isZero()
// Tests If Input Value Is Close To Zero
inline bool Math::isZero( float fValue ) {
    if ( (fValue > -ZERO) && (fValue < ZERO) ) {
        return true;
    }
    return false;
} // isZero

// -----------------------------------------------------------------------
// sign()
// Returns 1 If Value Is Positive, -1 If Value Is Negative Or 0 Otherwise
inline float Math::sign( float fValue ) {
    if ( fValue > 0 ) {
        return 1.0f;
    } else if ( fValue < 0 ) {
        return -1.0f;
    }
    return 0;
} // sign

// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between iMin And iMax Where iMin < iMax
inline int Math::randomRange( int iMin, int iMax ) {
    if ( iMax < iMin ) {
        swap( iMax, iMin );
     }
     return (iMin + ((iMax - iMin +1) * rand()) / (RAND_MAX+1) );
} // randomRange

// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between fMin And fMax Where fMin < fMax
inline float Math::randomRange( float fMin, float fMax ) {
    if ( fMax < fMin ) {
        swap( fMax, fMin );
    }
    return (fMin + (rand()/(float)RAND_MAX)*(fMax-fMin));
} // randomRange

// -----------------------------------------------------------------------
// mapValue()
// Returns The fValueY That Corresponds To A Point On The Line Going From Min To Max
inline float Math::mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX ) {
    if ( fValueX >= fMaxX ) {
        return fMaxY;
} else if ( fValueX <= fMinX ) {
        return fMinY;
    } else {
        float fM = (fMaxY - fMinY) / (fMaxX - fMinX);
        float fB = fMaxY - fM * fMaxX;
        return (fM*fValueX + fB);
    }
} // mapValue

// -----------------------------------------------------------------------
// constrain()
// Prevent Value From Going Outside The Min, Max Range.
template<class T>
inline void Math::constrain( T min, T max, T &value ) {
    if ( value < min ) {
        value = min;
        return;
    }
    if ( value > max ) {
        value = max;
    }
} // Constrain

// -----------------------------------------------------------------------
// swap()
//
template<class T>
inline void Math::swap( T &value1, T &value2 ) {
    T temp;
    temp   = value1;
    value1 = value2;
    value2 = temp;
} // swap

#endif // GENERALMATH_H

GeneralMath.cpp

#include "GeneralMath.h"

const float Math::PI           = 4.0f  * atan(1.0f); // tan(pi/4) = 1
const float Math::PI_HALVES    = 0.50f * Math::PI;
const float Math::PI_THIRDS    = Math::PI * 0.3333333333333f;
const float Math::PI_FOURTHS   = 0.25f * Math::PI;
const float Math::PI_SIXTHS    = Math::PI * 0.6666666666667f;
const float Math::PI_2         = 2.00f * Math::PI;
const float Math::PI_DIV180    = Math::PI / 180.0f;
const float Math::PI_INVx180   = 180.0f / Math::PI;
const float Math::PI_INV       = 1.0f / Math::PI;
const float Math::ZERO         = (float)1e-7;

// -----------------------------------------------------------------------
// Math()
// Default Constructor
Math::Math() {
} // Math

如果你在Vector3类对象上发出通知,它有2个方法返回向量的长度。一个使用sqrt(),它将给出绝对长度,但使用起来更昂贵。第二种方法length2()不使用sqrt()并返回廉价的相对长度。这个相同的概念可以应用于找到两点之间的距离!我希望这堂课能帮助你。如果您需要知道确切的长度或距离,那么使用sqrt()版本。当涉及多个点或矢量之间的比较时,A&amp;之间的比较真值表。 B使用任何一种方法都是相同的,因此最好使用此函数的第二版。