C言語関係掲示板

過去ログ

No.974 多次元ベクトルの内積(C++)

[戻る] [ホームページ]
No.12629

多次元ベクトルの内積
投稿者---エリ(2004/02/10 19:23:22)


多次元ベクトルの内積を使ったプログラムを
作ろうと思ったのですが、
構造体“vector型”と、
「2つの“vector型”の引数を取り、それらの内積を返す関数」
を含むようなプログラムを作りたいのですが、
具体的にどのようなプログラムに書いたらいいのでしょう。

自分でも作ってみたのですが、うまく実行できないので
どなたか教えていただけないでしょうか。
宜しくお願いします

#include <iostream>
    #include <math.h>
    
    /**************************/
    /*     Vectorクラスの定義 */
    /**************************/
    class Vector {
        public:
            int n;
            double *v;
            Vector (int n1)   // コンストラクタ
            {
                n = n1;
                v = new double [n];
            }
            ~Vector()   // デストラクタ
            {
                if (n > 0)
                    delete [] v;
            }
            double zettai();   // 絶対値
            double naiseki(Vector &);   // 内積
            void input();   // 要素の入力
    };
    
    /**************/
    /*     絶対値 */
    /**************/
    double Vector::zettai()
    {
        double x = 0.0;
        int i1;
        for (i1 = 0; i1 < n; i1++)
            x += v[i1] * v[i1];
        return sqrt(x);
    }
    
    /*************************/
    /*     内積              */
    /*          b : ベクトル */
    /*************************/
    double Vector::naiseki(Vector &b)
    {
        double x = 0.0;
        int i1;
        for (i1 = 0; i1 < n; i1++)
            x += v[i1] * b.v[i1];
        return x;
    }
    
    /************/
    /*     入力 */
    /************/
    void Vector::input()
    {
        int i1;
        for (i1 = 0; i1 < n; i1++) {
            std::cout << "   " << i1+1 << "番目の要素は? ";
            std::cin >> v[i1];
        }
    }
    
    /**********************/
    /*     mainプログラム */
    /**********************/
    int main()
    {
        Vector a(2), b(2);
    
        std::cout << "a\n";
        a.input();
        std::cout << "b\n";
        b.input();
    
        std::cout << "内積 " << a.naiseki(b) << std::endl;
        std::cout << "絶対値 " << a.zettai() << " " << b.zettai() << std::endl;
    
        return 0;
    }



No.12631

Re:多次元ベクトルの内積
投稿者---iijima(2004/02/10 20:36:51)


>「2つの“vector型”の引数を取り、それらの内積を返す関数」

すでに作成されたVectorクラスを使うならば,まず,Vectorクラスのnaiseki関数にconst修飾子をつけたうえで...

class Vector
{
...
    double naiseki( const Vector& b ) const;
};


次のような関数を定義するのではいかがでしょうか.

double naiseki( const Vector& a, const Vector& b )
{
     a.naiseki( b );
}


なお,本題ではありませんがVectorクラスにはコピーコンストラクタと代入演算子を定義すべきですね.
∵データメンバーvが指すメモリ領域は,オブジェクト内で動的確保・解放されるように定義されている.

No.12632

Re:多次元ベクトルの内積
投稿者---たか(2004/02/10 20:37:13)


>「2つの“vector型”の引数を取り、それらの内積を返す関数」
>を含むようなプログラムを作りたいのですが、

friend関数にすればよいでしょう。そうすればprivateメンバにアクセス
できます。ちょっと今手が離せなくてすみません。

No.12633

Re:多次元ベクトルの内積
投稿者---たか(2004/02/10 20:38:09)


うお、よく見たらデータメンバもpublicでしたか。private:にした方が
本来のクラスの使い方に近いです。

No.12636

Re:多次元ベクトルの内積
投稿者---エリ(2004/02/10 21:46:57)


>friend関数にすればよいでしょう。そうすればprivateメンバにアクセス
>できます。ちょっと今手が離せなくてすみません。

すいませんここの説明がちょっと分かりずらいのですが
どのように書き換えたらいいのでしょうか?


No.12639

Re:多次元ベクトルの内積
投稿者---YuO(2004/02/10 22:17:00)


>>>多次元ベクトルの内積を使ったプログラムを
>>>作ろうと思ったのですが、
>>>構造体“vector型”と、
>>>「2つの“vector型”の引数を取り、それらの内積を返す関数」
>>>を含むようなプログラムを作りたいのですが、
>>>具体的にどのようなプログラムに書いたらいいのでしょう。

大文字・小文字は区別して下さいね。
標準C++ライブラリにvectorクラスがありますから。

ちなみに,標準C++ライブラリには内積を計算するための数学アルゴリズムが既に存在します。
#絶対値もそれを使って簡単に計算できたりする。


>>friend関数にすればよいでしょう。そうすればprivateメンバにアクセス
>>できます。ちょっと今手が離せなくてすみません。

というか,friendにしなくてもVectorの各要素にアクセスできるメンバ関数が必要になるでしょうから,
それを使えばよいですね。


>すいませんここの説明がちょっと分かりずらいのですが
>どのように書き換えたらいいのでしょうか?

思いつくがままに作ってみました。
どうせなら,ということでクラステンプレートです。
#include <algorithm> // for copy
#include <numeric>   // for inner_product
#include <cassert>   // for assert
#include <cmath>     // for sqrt
#include <cstddef>   // for size_t

template <typename T> class Vector {
private:
    std::size_t size_;
    T * data_;

public:
    Vector (std::size_t size) :
        size_(size),
        data_(new T [size_])
    {
        assert(size_ != 0);
    }
    Vector (const Vector & obj) :
        size_(obj.size_),
        data_(new T [size_])
    {
        std::copy(obj.data_, obj.data_ + size_, data_);
    }

    ~Vector (void)
    {
        delete []data_;
    }

    Vector& operator= (const Vector & rhs)
    {
        if (size_ != rhs.size_) {
            T * ptr = new T [rhs.size_];
            delete []data_;
            data_ = ptr;
            size_ = rhs.size_;
        }
        if (&rhs != this) {
            std::copy(rhs.data_, rhs.data_ + size_, data_);
        }
        return *this;
    }

    T abs (void) const // 絶対値
    {
        return std::sqrt(std::inner_product(data_, data_ + size_, data_, T()));
    }

    T & operator[] (std::size_t index) // 要素へのアクセス
    {
        return data_[index];
    }
    const T & operator[] (std::size_t index) const
    {
        return data_[index];
    }

    std::size_t size (void) const
    {
        return size_;
    }
};

template <typename T>
T innerProduct (const Vector<T> & lhs, const Vector<T> & rhs) // 内積
{
    assert(lhs.size() == rhs.size());
    return std::inner_product(&lhs[0], &lhs[0] + lhs.size(), &rhs[0], T());
}


ちなみに,VC++の6.0までとかのように,標準Cライブラリがstd名前空間に入っていないコンパイラでは,
namespace std {
    using sqrt;
    using size_t;
}

を先頭に突っ込んでおく必要があります。
#size_tはstd::消すだけで良いけれど,sqrtはこれをする必要がある<std::complexなどに対する対策。

No.12640

Re:多次元ベクトルの内積
投稿者---たか(2004/02/10 22:36:06)


>>friend関数にすればよいでしょう。そうすればprivateメンバにアクセス
>>できます。ちょっと今手が離せなくてすみません。
>
>すいませんここの説明がちょっと分かりずらいのですが
>どのように書き換えたらいいのでしょうか?

いえ、YuOさんのおっしゃられる通り、このクラスのデータメンバはすべ
てpublic:属性ですからfriendにする必要はありません。

No.12654

Re:多次元ベクトルの内積
投稿者---たか(2004/02/11 16:11:08)


私もクラスを作ってみましたのでよろしかったら見てみてください。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <new>
    
/**************************/
/*     Vectorクラスの定義 */
/**************************/
class MyVector {
private:
  int n;
  double *v;
public:
  friend double naiseki(const MyVector& a, const MyVector& b);
  MyVector(int n1 = 1) : n(n1), v(0) { v = new double [n]; }
  ~MyVector() { delete[] v; }
  MyVector(const MyVector& my) {
     new (this) MyVector(my.n); 
     std::copy(my.v, &my.v[my.n], v);
  }
  MyVector& operator=(const MyVector& my) {
    new (this) MyVector(my.n);
    std::copy(my.v, &my.v[my.n], v);
    return *this;
  }
  double zettai() const;                  // 絶対値
  double naiseki(const MyVector&) const;  // 内積
  void input();                           // 要素の入力
};

double pow2(double value, double d)
{
  return value + d * d;
}

/**************/
/*     絶対値 */
/**************/
double MyVector::zettai() const
{
  double x = std::accumulate(v, &v[n], 0, pow2);

  return std::sqrt(x);
}
    
/*************************/
/*     内積              */
/*          b : ベクトル */
/*************************/
double MyVector::naiseki(const MyVector& b) const
{
  return std::inner_product(v, &v[n], b.v, 0);
}

/************/
/*     入力 */
/************/
void MyVector::input()
{
  for (int i1 = 0; i1 < n; i1++) {
    std::cout << "   " << (i1 + 1) << "番目の要素は? ";
    std::cin >> v[i1];
  }
}

/*************************/
/*     内積              */
/*       a, b : ベクトル */
/*************************/
double naiseki(const MyVector& a, const MyVector& b)
{
  return std::inner_product(a.v, &a.v[a.n], b.v, 0);
}


/**********************/
/*     mainプログラム */
/**********************/
int main()
{
  MyVector a(2), b(2);

  std::cout << "a\n";
  a.input();
  std::cout << "b\n";
  b.input();

  std::cout << "内積 " << naiseki(a, b) << std::endl;
  std::cout << "内積(メンバ関数) " << a.naiseki(b) << std::endl;
  std::cout << "絶対値 a=" << a.zettai() << ", b=" << b.zettai() << std::endl;
}


No.12656

Re:多次元ベクトルの内積
投稿者---YuO(2004/02/11 16:42:52)


  MyVector& operator=(const MyVector& my) {
    new (this) MyVector(my.n);
    std::copy(my.v, &my.v[my.n], v);
    return *this;
  }


これはメモリリークしますよ。
元々の要素が破棄されていないのに,それらの所有権を放棄してしまいますから。

さらに,自分自身への代入が起きたときに,値が壊れます。


No.12657

Re:多次元ベクトルの内積
投稿者---たか(2004/02/11 17:17:30)


  MyVector& operator=(const MyVector& my) {
    new (this) MyVector(my.n);
    std::copy(my.v, &my.v[my.n], v);
    return *this;
}


>これはメモリリークしますよ。
>元々の要素が破棄されていないのに,それらの所有権を放棄してしまいますから。
>
>さらに,自分自身への代入が起きたときに,値が壊れます。

そうでした。それと自己代入チェック忘れてました。
ご指摘ありがとうございます。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <new>
    
/**************************/
/*     Vectorクラスの定義 */
/**************************/
class MyVector {
private:
  int n;
  double *v;
public:
  friend double naiseki(const MyVector& a, const MyVector& b);
  MyVector(int n1 = 1) : n(n1), v(0) { v = new double[n]; }
  ~MyVector() { delete[] v; }
  MyVector(const MyVector& my) {
     new (this) MyVector(my.n); 
     std::copy(my.v, &my.v[n], v);
  }
  MyVector& operator=(const MyVector& my) {
    if (this != &my) {
      delete[] v;
      new (this) MyVector(my.n);
      std::copy(my.v, &my.v[n], v);
    }
    return *this;
  }
  double zettai() const;                  // 絶対値
  double naiseki(const MyVector&) const;  // 内積
  void input();                           // 要素の入力
};

double pow2(double value, double d)
{
  return value + d * d;
}

/**************/
/*     絶対値 */
/**************/
double MyVector::zettai() const
{
  double x = std::accumulate(v, &v[n], 0, pow2);

  return std::sqrt(x);
}
    
/*************************/
/*     内積              */
/*          b : ベクトル */
/*************************/
double MyVector::naiseki(const MyVector& b) const
{
  return std::inner_product(v, &v[n], b.v, 0);
}

/************/
/*     入力 */
/************/
void MyVector::input()
{
  for (int i1 = 0; i1 < n; i1++) {
    std::cout << "   " << (i1 + 1) << "番目の要素は? ";
    std::cin >> v[i1];
  }
}

/*************************/
/*     内積              */
/*       a, b : ベクトル */
/*************************/
double naiseki(const MyVector& a, const MyVector& b)
{
  return std::inner_product(a.v, &a.v[a.n], b.v, 0);
}


/**********************/
/*     mainプログラム */
/**********************/
int main()
{
  MyVector a(2), b(2);

  std::cout << "a\n";
  a.input();
  std::cout << "b\n";
  b.input();

  std::cout << "内積 " << naiseki(a, b) << std::endl;
  std::cout << "内積(メンバ関数) " << a.naiseki(b) << std::endl;
  std::cout << "絶対値 a=" << a.zettai() << ", b=" << b.zettai() << std::endl;
}


No.12662

Re:多次元ベクトルの内積
投稿者---YuO(2004/02/11 22:33:23)


今気付いたのですが……。


/**************/
/*     絶対値 */
/**************/
double MyVector::zettai() const
{
  double x = std::accumulate(v, &v[n], 0, pow2);

  return std::sqrt(x);
}
/*************************/
/*     内積              */
/*          b : ベクトル */
/*************************/
double MyVector::naiseki(const MyVector& b) const
{
  return std::inner_product(v, &v[n], b.v, 0);
}
/*************************/
/*     内積              */
/*       a, b : ベクトル */
/*************************/
double naiseki(const MyVector& a, const MyVector& b)
{
  return std::inner_product(a.v, &a.v[a.n], b.v, 0);
}


初期値が0だと,std::accumulate<const double *, int>とか
std::inner_product<const double *, const double *, int>が呼び出されてしまいますね。

というわけで,0ではなく0.0にする必要があります。