[topic] 分数の発見
Posted feedbacks - C++
boost::math::gcd で既約分数のチェックは楽できました。
それ以外は普通?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | #include <iostream>
#include <vector>
#include <functional>
#include <cmath>
#include <iterator>
#include <string>
#include <stdexcept>
#include <boost/math/common_factor_rt.hpp>
#include <boost/lexical_cast.hpp>
class rational_t
{
int n_, d_;
double r_;
public:
rational_t(int numerator, int denominator)
: n_(numerator), d_(denominator),
r_(static_cast<double>(n_)/static_cast<double>(d_)) {}
double real() const
{ return r_; }
bool isnan() const
{ return std::isnan(r_); }
int numerator() const
{ return n_; }
int denominator() const
{ return d_; }
};
std::ostream&
operator<<(std::ostream& os, const rational_t& r)
{
return (os << r.numerator() << "/" << r.denominator());
}
class compare_near_to
: public std::binary_function<bool, rational_t, rational_t>
{
double g_;
public:
compare_near_to(double goal) : g_(goal) {}
bool operator()(
const rational_t& lhs,
const rational_t& rhs
)
{
return std::fabs(g_-lhs.real()) < std::fabs(g_-rhs.real());
}
};
rational_t
find_rational(
double goal,
int denominator
)
{
double min_diff = goal;
rational_t rat(0, denominator);
for ( int n = 1; ; ++n ) {
rational_t rat_(n, denominator);
double diff = std::fabs(goal - rat_.real());
if ( diff > min_diff ) break;
min_diff = diff;
rat = rat_;
}
if ( boost::math::gcd(rat.numerator(), rat.denominator()) != 1 )
return rational_t(0,0); // causes real is NaN
return rat;
}
int main(int c, char** v)
{
if ( c != 2 ) {
std::cout << v[0] << " <real number|rational number>\n";
return 0;
}
double goal;
try {
std::string val(v[1]);
std::string::size_type idx;
if ( (idx = val.find("/")) != std::string::npos )
goal = boost::lexical_cast<double>(val.substr(0,idx)) /
boost::lexical_cast<double>(val.substr(idx+1,val.length()-(idx+1)));
else
goal = boost::lexical_cast<double>(v[1]);
if ( goal <= 0.0 )
throw std::runtime_error("input number must be positive");
}
catch (const std::exception& e) {
std::cerr << e.what() << "\n";
return -1;
}
std::vector<rational_t> rationals;
for ( int i = 1; i < 10; ++i )
rationals.push_back(find_rational(goal, i));
rationals.erase(
std::remove_if(
rationals.begin(),
rationals.end(),
std::mem_fun_ref(&rational_t::isnan) ),
rationals.end());
std::sort(
rationals.begin(),
rationals.end(),
compare_near_to(goal) );
std::copy(
rationals.begin(),
rationals.end(),
std::ostream_iterator<rational_t>(std::cout, ", ") );
return 0;
}
|


gandalf #6278() Rating1/1=1.00
schemeにはrationalizeという手続きがありますが、これは結果を一つしか返しません。そして、複数の結果が欲しい場合も稀にあります。
そこで、非負の実数aが一つ与えられたときに、以下の条件を満たす分数b/cをaに近い順に全て表示する手続きを考えてみてください。条件は、 1. 分数b/cよりaに近い分数d/cは存在しない 2. 分数b/cは既約 3. cは1桁の整数 です。
例をいくつかあげます(あってると思うけど...)。
a = 1.732051 12/7 7/4 16/9 5/3 9/5 3/2 2/1
a = 3.141593 22/7 25/8 19/6 28/9 16/5 13/4 3/1
a = 1920 / 1080 16/9 9/5 7/4 11/6 12/7 5/3 2/1
[ reply ]