valarrayとは

valarrayとは、その名の通り値を扱う配列の様なものなのですが、vectorなどとは異なり、行列式・行列・数列などの一連の数値の扱いと、その演算に特化したクラスであるといえます。 まず始めに、基本的な使い方を見ることでその概要をつかんでみようと思います。

#include <iostream>
#include <valarray>

using namespace std;

int main()
{
    valarray <int> arr(10);

    size_t i;
    size_t s = arr.size();

    // valarrayへの要素の代入
    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    // valarrayからの要素の取得
    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;

    return 0;
}
実行結果
0 1 2 3 4 5 6 7 8 9
Press any key to continue

このように、普通の配列同様に扱えます。 しかしクラスであるためにsize()関数が存在します。 valarrayにはこのほかにも様々なメンバ関数が存在します。

valarrayに対する四則演算

普通の配列とは異なり、valarrayでは、valarrayに対して直接四則演算を行うことができます。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(10);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = i;
    }

    cout << "arr: "; EnumerateValarray( arr );

    // valarrayに対する四則演算
    cout << "arr + 3: "; EnumerateValarray( arr + 3 );
    cout << "arr - 5: "; EnumerateValarray( arr - 5 );
    cout << "arr * 4: "; EnumerateValarray( arr * 4 );
    cout << "arr / 2: "; EnumerateValarray( arr / 2 );

    return 0;
}
実行結果
arr: 0 1 2 3 4 5 6 7 8 9
arr + 3: 3 4 5 6 7 8 9 10 11 12
arr - 5: -5 -4 -3 -2 -1 0 1 2 3 4
arr * 4: 0 4 8 12 16 20 24 28 32 36
arr / 2: 0 0 1 1 2 2 3 3 4 4
Press any key to continue

このサンプルのように、valarrayに対して直接加減乗除を行うと、その要素一つ一つに同じ値を加減乗除したのと同じ結果が得られます。 これがvalarrayの最大の特徴といえるでしょう。

valarray同士の四則演算

さらに、valarrayに対する四則演算の他にも、valarray同士の演算が行える特徴的な機能があります。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr1(10), arr2(10);

    size_t i;

    for ( i = 0; i < 10; i++ )
    {
        arr1[i] = i;
        arr2[i] = 10 - i;
    }

    // 四則演算
    cout << "arr1: "; EnumerateValarray( arr1 );
    cout << "arr2: "; EnumerateValarray( arr2 );
    cout << endl;

    cout << "arr1 + arr2: "; EnumerateValarray( arr1 + arr2 );
    cout << "arr1 - arr2: "; EnumerateValarray( arr1 - arr2 );
    cout << "arr1 * arr2: "; EnumerateValarray( arr1 * arr2 );
    cout << "arr1 / arr2: "; EnumerateValarray( arr1 / arr2 );
    cout << "arr1 % arr2: "; EnumerateValarray( arr1 % arr2 );
    cout << endl;

    // 複合代入演算
    cout << "arr1: "; EnumerateValarray( arr1 );
    cout << "arr1 += 10 : "; EnumerateValarray( arr1 += 10 );
    cout << "arr1 -= 5 : ";  EnumerateValarray( arr1 -= 5 );
    cout << endl;

    cout << "arr2: "; EnumerateValarray( arr2 );
    cout << "arr2 *= 6 : ";  EnumerateValarray( arr2 *= 6 );
    cout << "arr2 /= 2 : ";  EnumerateValarray( arr2 /= 2 );
    cout << endl;

    return 0;
}
実行結果
arr1: 0 1 2 3 4 5 6 7 8 9
arr2: 10 9 8 7 6 5 4 3 2 1

arr1 + arr2: 10 10 10 10 10 10 10 10 10 10
arr1 - arr2: -10 -8 -6 -4 -2 0 2 4 6 8
arr1 * arr2: 0 9 16 21 24 25 24 21 16 9
arr1 / arr2: 0 0 0 0 0 1 1 2 4 9
arr1 % arr2: 0 1 2 3 4 0 2 1 0 0

arr1: 0 1 2 3 4 5 6 7 8 9
arr1 += 10 : 10 11 12 13 14 15 16 17 18 19
arr1 -= 5 : 5 6 7 8 9 10 11 12 13 14

arr2: 10 9 8 7 6 5 4 3 2 1
arr2 *= 6 : 60 54 48 42 36 30 24 18 12 6
arr2 /= 2 : 30 27 24 21 18 15 12 9 6 3

Press any key to continue

このサンプルでは、valarray同士の加減乗除および、複合代入演算を行っています。 参考までに、valarray同士の要素数は同じである必要があります。 異なる要素数の場合、演算の結果は未定義です。

valarrayのメンバ

valarrayはクラスなので、当然メンバ関数が存在します。 次のコードはその関数を利用したサンプルです。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr(10);

    size_t i;

    for ( i = 0; i < arr.size(); i++ )
    {
        arr[i] = i;
    }

    // 値の列挙
    cout << "arr: "; EnumerateValarray( arr );
    cout << endl;

    // valarrayのメンバ
    cout << "size of arr is " << arr.size() << endl;
    cout << "sum of arr is " << arr.sum() << endl;
    cout << "maximum value of arr is " << arr.max() << endl;
    cout << "minimum value of arr is " << arr.min() << endl;
    cout << endl;

    cout << "shifts the elements left 5 places: " << endl;  EnumerateValarray( arr.shift(5) );
    cout << "shifts the elements right 5 places: " << endl; EnumerateValarray( arr.shift(-5) );
    cout << endl;

    cout << "shifts the elements cyclically left 3 places: " << endl;  EnumerateValarray( arr.cshift(3) );
    cout << "shifts the elements cyclically right 3 places: " << endl; EnumerateValarray( arr.cshift(-3) );
    cout << endl;

    return 0;
}
実行結果
arr1: 0 1 2 3 4 5 6 7 8 9
arr2: 10 9 8 7 6 5 4 3 2 1

arr1 + arr2: 10 10 10 10 10 10 10 10 10 10
arr1 - arr2: -10 -8 -6 -4 -2 0 2 4 6 8
arr1 * arr2: 0 9 16 21 24 25 24 21 16 9
arr1 / arr2: 0 0 0 0 0 1 1 2 4 9
arr1 % arr2: 0 1 2 3 4 0 2 1 0 0

arr1: 0 1 2 3 4 5 6 7 8 9
arr1 += 10 : 10 11 12 13 14 15 16 17 18 19
arr: 0 1 2 3 4 5 6 7 8 9

size of arr is 10
sum of arr is 45
maximum value of arr is 9
minimum value of arr is 0

shifts the elements left 5 places:
5 6 7 8 9 0 0 0 0 0
shifts the elements right 5 places:
0 0 0 0 0 0 1 2 3 4

shifts the elements cyclically left 3 places:
3 4 5 6 7 8 9 0 1 2
shifts the elements cyclically right 3 places:
7 8 9 0 1 2 3 4 5 6

Press any key to continue

このようにvalarrayのサイズを求める関数や、valarrayに格納されている数値の合計、最大値、最小値を求める関数、valarray内の数値の並びをシフトする関数など、便利なメンバ関数が用意されています。

valarrayのリサイズ

途中でvalarrayのサイズを変えたい場合はresize()関数を使います。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <int> arr1(10), arr2(10);

    size_t i;

    for ( i = 0; i < 10; i++ )
    {
        arr1[i] = i;
        arr2[i] = 10 - i;
    }

    // valarrayのリサイズ
    arr1.resize( 15, 20 );
    cout << "resize arr1: " << endl; EnumerateValarray( arr1 );

    arr2.resize( 5, 0 );
    cout << "resize arr2: " << endl; EnumerateValarray( arr2 );

    cout << endl;

    return 0;
}
実行結果
resize arr1:
0 1 2 3 4 5 6 7 8 9 20 20 20 20 20
resize arr2:
10 9 8 7 6

Press any key to continue

このように、リサイズする際に、拡張された部分を満たす値を設定することができます。 この例では拡張の際に20という数値で満たしています。 また、拡張だけでなく縮小する事もできます。

valarrayに対する三角関数の利用

valarrayに対して、四則演算だけでなく、三角関数などの超越関数を利用した演算も行うことができます。

#include <iostream>
#include <iomanip>
#include <valarray>
#include <cmath>

#define pi 3.14159265359

using namespace std;

template <class _T> void EnumerateValarrayAngle( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << setw( 10 ) << arr[i] << "[rad] : " << setw( 5 ) << ( arr[i] / pi * 180.0 ) << "[deg]" << endl;
    }
}

int main()
{
    valarray <double> Angle(12);

    size_t i;
    size_t s = Angle.size();

    for ( i = 0; i < s; i++ )
    {
        Angle[i] = pi * (double)i / (double)s;
    }

    EnumerateValarrayAngle( Angle );

    cout << endl;

    // 三角関数の演算
    valarray <double> Sin( s );
    valarray <double> Cos( s );
    valarray <double> Tan( s );

    Sin = sin( Angle );
    Cos = cos( Angle );
    Tan = tan( Angle );

    cout << setw(15) << "theta[rad]" << setw(15) << "sin" << setw(15) << "cos" << setw(15) << "tan" << endl;

    for ( i = 0; i < s; i++ )
    {
        cout << setw(15) << Angle[i]
             << setw(15) << Sin[i]
             << setw(15) << Cos[i]
             << setw(15) << Tan[i] << endl;
    }

    return 0;
}
実行結果
         0[rad] :     0[deg]
  0.261799[rad] :    15[deg]
  0.523599[rad] :    30[deg]
  0.785398[rad] :    45[deg]
    1.0472[rad] :    60[deg]
     1.309[rad] :    75[deg]
    1.5708[rad] :    90[deg]
    1.8326[rad] :   105[deg]
    2.0944[rad] :   120[deg]
   2.35619[rad] :   135[deg]
   2.61799[rad] :   150[deg]
   2.87979[rad] :   165[deg]

     theta[rad]            sin            cos            tan
              0              0              1              0
       0.261799       0.258819       0.965926       0.267949
       0.523599            0.5       0.866025        0.57735
       0.785398       0.707107       0.707107              1
         1.0472       0.866025            0.5        1.73205
          1.309       0.965926       0.258819        3.73205
         1.5708              1  -1.03634e-013  -9.64938e+012
         1.8326       0.965926      -0.258819       -3.73205
         2.0944       0.866025           -0.5       -1.73205
        2.35619       0.707107      -0.707107             -1
        2.61799            0.5      -0.866025       -0.57735
        2.87979       0.258819      -0.965926      -0.267949
Press any key to continue

この結果を見てわかるように、valarrayに対していとも簡単に三角関数による演算が行えます。 三角関数の他にも逆三角関数・双曲線関数ももちろん利用できます。

valarrayに対するその他の超越関数の演算

三角関数・逆三角関数の他の超越関数として対数関数・指数関数など以下の関数も利用できます。

#include <iostream>
#include <valarray>
#include <cmath>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    valarray <double> arr(10);

    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        arr[i] = (double)i;
    }

    cout << "arr: "; EnumerateValarray( arr );
    cout << endl;

    // pow, sqrt, exp, log, log10の適用
    arr = pow( arr, 2.0 );
    cout << "pow( arr, 2 ): "; EnumerateValarray( arr );

    arr = sqrt( arr );
    cout << "sqrt( arr ): "; EnumerateValarray( arr );

    arr = exp( arr );
    cout << "exp( arr ): "; EnumerateValarray( arr );

    arr = log( arr );
    cout << "log( arr ): "; EnumerateValarray( arr );

    arr = log10( arr );
    cout << "log10( arr ): "; EnumerateValarray( arr );

    return 0;
}
実行結果
arr: 0 1 2 3 4 5 6 7 8 9

pow( arr, 2 ): 0 1 4 9 16 25 36 49 64 81
sqrt( arr ): 0 1 2 3 4 5 6 7 8 9
exp( arr ): 1 2.71828 7.38906 20.0855 54.5982 148.413 403.429 1096.63 2980.96 81
03.08
log( arr ): 0 1 2 3 4 5 6 7 8 9
log10( arr ): -1.#INF 0 0.30103 0.477121 0.60206 0.69897 0.778151 0.845098 0.903
09 0.954243
Press any key to continue

このように、expやlogなどの超越関数もdouble型やint型に対する演算と同様に使用することができます。

valarrayに対する独自定義の演算

今までのサンプルで紹介したような定義された演算の他にも、独自にvalarrayに対する演算を定義することができます。

#include <iostream>
#include <valarray>

using namespace std;

template <class _T> void EnumerateValarray( valarray<_T>& arr )
{
    size_t i;
    size_t s = arr.size();

    for ( i = 0; i < s; i++ )
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// 独自定義の演算用関数 (加算)
int __cdecl applyProcAddition( int val )
{
    return val + 5;
}

// 独自定義の演算用関数 (減算)
int __cdecl applyProcSubtraction( int val )
{
    return val - 3;
}

int main()
{
    valarray <int> arr1(10), arr2(10);

    size_t i;

    for ( i = 0; i < 10; i++ )
    {
        arr1[i] = i;
        arr2[i] = 10 - i;
    }

    // 独自定義の演算
    cout << "arr1: "; EnumerateValarray( arr1 );
    cout << "arr1.apply() : "; EnumerateValarray( arr1.apply( applyProcAddition ) );
    cout << endl;

    cout << "arr2: "; EnumerateValarray( arr2 );
    cout << "arr2.apply() : "; EnumerateValarray( arr2.apply( applyProcSubtraction ) );
    cout << endl;

    return 0;
}
実行結果
arr1: 0 1 2 3 4 5 6 7 8 9
arr1.apply() : 5 6 7 8 9 10 11 12 13 14

arr2: 10 9 8 7 6 5 4 3 2 1
arr2.apply() : 7 6 5 4 3 2 1 0 -1 -2

Press any key to continue

このサンプルのように、valarrayのapply()関数に演算を定義した独自のコールバック関数を指定すれば、valarrayに対する演算を定義できます。 この例では加算と減算という単純な演算だけを行わせましたが、複雑な演算を行いたい場合には有用な方法といえます。