Boostライブラリを活用した高度なC++プログラミング
C++のBoostライブラリは、非常に幅広い用途に活用できる強力なライブラリ群です。多倍長整数の計算から高精度な浮動小数点演算まで、標準ライブラリだけでは難しい処理を簡潔に実装できます。たとえば、Boostを使えばC++でも264を超えるような巨大な数値を問題なく扱えるようになります。
本記事では、Boostライブラリの代表的な機能を、実際のサンプルコードとともに紹介します。
巨大な整数を扱う固定幅のデータ型
Boost.Multiprecisionでは、int128_t、int256_t、int1024_tといった多倍長整数型が用意されており、これらを使えば最大1024ビット精度の整数演算を簡単に行えます。
最初の例として、Boostライブラリを使って2つの巨大な数値を掛け合わせてみましょう。
サンプルコード
#include<iostream>
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
using namespace std;
int128_t large_product(long long n1, long long n2) {
int128_t ans = (int128_t) n1 * n2;
return ans;
}
int main() {
long long num1 = 98745636214564698;
long long num2 = 7459874565236544789;
cout << "Product of " << num1 << " * " << num2 << " = "
<< large_product(num1, num2);
}出力結果
Product of 98745636214564698 * 7459874565236544789 = 736630060025131838840151335215258722
long long型では表現しきれない桁数の積も、int128_tにキャストして計算すれば正確な結果が得られます。
任意精度整数型cpp_int
もうひとつ便利なのが「任意精度データ型」です。cpp_int型を使えば、必要な精度が実行時に自動的に割り当てられるため、桁あふれを気にせずプログラムを書けます。次の例では、cpp_intを使って50の階乗を計算しています。
サンプルコード
#include<iostream>
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
using namespace std;
cpp_int large_fact(int num) {
cpp_int fact = 1;
for (int i = num; i > 1; --i)
fact *= i;
return fact;
}
int main() {
cout << "Factorial of 50: " << large_fact(50) << endl;
}出力結果
Factorial of 50: 30414093201713378043612608166064768844377641568960512000000000000
50!(50の階乗)は67桁にもなる巨大な数ですが、cpp_intなら特別な処理を一切せずに正確に求められます。
多倍長浮動小数点数による高精度計算
Boostの多倍長浮動小数点型を使えば、小数点以下50桁や100桁といった高精度の計算も可能です。それぞれcpp_dec_float_50、cpp_dec_float_100という型を使用します。
次の例では、float、double、そしてcpp_dec_float_50の3種類の型で半径2.43の円の面積を計算し、それぞれの精度の違いを比較してみます。
サンプルコード
#include<iostream>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/math/constants/constants.hpp>
using boost::multiprecision::cpp_dec_float_50;
using namespace std;
template<typename T>
inline T circle_area(T r) {
// piはBoostが提供する事前定義定数
using boost::math::constants::pi;
return pi<T>() * r * r;
}
int main() {
float f_rad = 243.0 / 100;
float f_area = circle_area(f_rad);
double d_rad = 243.0 / 100;
double d_area = circle_area(d_rad);
cpp_dec_float_50 rad_mp = 243.0 / 100;
cpp_dec_float_50 area_mp = circle_area(rad_mp);
cout << "Float: " << setprecision(numeric_limits<float>::digits10) << f_area << endl;
// doubleでの面積
cout << "Double: " << setprecision(numeric_limits<double>::digits10) << d_area << endl;
// Boost Multiprecisionによる面積
cout << "Boost Multiprecision Res: "
<< setprecision(numeric_limits<cpp_dec_float_50>::digits10) << area_mp << endl;
}出力結果
Float: 18.5508 Double: 18.5507904601824 Boost Multiprecision Res: 18.550790460182372534747952560288165408707655564121
このように、同じ計算でも使用する型によって得られる精度は大きく異なります。floatでは小数第5位程度までしか正確に求められませんが、cpp_dec_float_50を使えば小数点以下50桁もの精度が得られます。科学技術計算や金融計算など、わずかな誤差も許されない場面では、Boost.Multiprecisionが大きな威力を発揮するでしょう。
-
C++で解く「3nスライスのピザ」問題 ― 動的計画法でスライスの合計を最大化する方法
問題の概要 大きさがまちまちの 3n 個のスライスからなるピザがあるとします。私と友人2人は、次のルールに従ってピザを取っていきます。 私が任意のスライスを1枚選びます。 友人のAmalは、私が選んだスライスの反時計回り方向に隣接するスライスを取ります。 友人のBimalは、私が選んだスライスの時計回り方向に隣接するスライスを取ります。 ピザのスライスがなくなるまで、この手順を繰り返します。 各スライスの大きさは、時計回りの順に並べた環状配列 slices として与えられます。求めるのは、私が手にできるスライスの大きさの合計の最大値です。 入出力例 入力が [9, 8, 6, 1, 1,
-
キャリー付き乗算法(MWC法)で乱数を生成するC++プログラムの実装方法
キャリー付き乗算法(MWC法)とはキャリー付き乗算法(Multiply-With-Carry、略称MWC法)は、1991年にMarsagliaとZamanによって提案されたキャリー付き加算法(Add-With-Carry)ジェネレータの変種です。この手法の最大の利点は、コンピュータが最も得意とする単純な整数演算だけで動作するため、非常に高速に乱数列を生成できる点にあります。さらに、生成される乱数列の周期は約260から22000000にまで及ぶ、きわめて長い周期を持つことも大きな特徴です。MWC法では、基数bはコンピュータのワードサイズと一致するように選ばれ、乗数aとラグr(遅延量)によって法p