Во-первых, нам нужно, чтобы некоторые из них включали доступ к нормальному распределению, алгоритмы для поиска местоположения и масштаба (и, конечно же, некоторый вывод std).
#include <boost/math/distributions/normal.hpp>
using boost::math::normal;
#include <boost/math/distributions/cauchy.hpp>
using boost::math::cauchy;
#include <boost/math/distributions/find_location.hpp>
using boost::math::find_location;
#include <boost/math/distributions/find_scale.hpp>
using boost::math::find_scale;
using boost::math::complement;
using boost::math::policies::policy;
#include <iostream>
using std::cout; using std::endl; using std::left; using std::showpoint; using std::noshowpoint;
#include <iomanip>
using std::setw; using std::setprecision;
#include <limits>
using std::numeric_limits;
#include <stdexcept>
Рассмотрим пример из K Krishnamoorthy, Handbook of Statistical Distributions with Applications, ISBN 1-58488-635-8, (2006) p 126, example 10.3.7.
"Машина должна упаковывать по 3 кг говядины на упаковку. За длительный период времени установлено, что средний упакованный вес составил 3 кг при стандартном отклонении 0,1 кг. Предположим, что упаковка обычно распределяется".
Начнем с построения нормального распределения с заданными параметрами:
double mean = 3.;
double standard_deviation = 0.1;
normal packs(mean, standard_deviation);
Затем мы можем найти долю (или %) упаковок, которые весят более 3,1 кг.
double max_weight = 3.1;
cout << "Percentage of packs > " << max_weight << " is "
<< cdf(complement(packs, max_weight)) * 100. << endl;
Мы можем захотеть убедиться, что 95% упаковок имеют минимальный вес, тогда мы хотим, чтобы среднее значение было таким, чтобы P(X< 2,9) = 0,05.
Используя среднее значение 3 кг, можно оценить долю упаковок, которые не соответствуют спецификации 2,9 кг.
double minimum_weight = 2.9;
cout <<"Fraction of packs <= " << minimum_weight << " with a mean of " << mean
<< " is " << cdf(complement(packs, minimum_weight)) << endl;
Это 0,84 - больше целевой фракции 0,95. Если мы хотим, чтобы 95% весили больше минимального, то какой должен быть средний вес?
С помощью программы KK StatCalc, поставляемой с книгой, и метода, приведенного на странице 126, выдается 3.06449.
Мы можем подтвердить это, построив новый дистрибутив, который мы называем «xpacks» со средним значением запаса прочности 3.06449.
double over_mean = 3.06449;
normal xpacks(over_mean, standard_deviation);
cout << "Fraction of packs >= " << minimum_weight
<< " with a mean of " << xpacks.mean()
<< " is " << cdf(complement(xpacks, minimum_weight)) << endl;
Используя этот набор математических инструментов, мы можем рассчитать требуемое среднее значение напрямую:
double under_fraction = 0.05;
double low_limit = standard_deviation;
double offset = mean - low_limit - quantile(packs, under_fraction);
double nominal_mean = mean + offset;
normal nominal_packs(nominal_mean, standard_deviation);
cout << "Setting the packer to " << nominal_mean << " will mean that "
<< "fraction of packs >= " << minimum_weight
<< " is " << cdf(complement(nominal_packs, minimum_weight)) << endl;
Этот расчет обобщается как свободная функция, называемаяfind_location.
, см.Алгоритмы.
Чтобы использовать это, нам нужно
#include <boost/math/distributions/find_location.hpp>
using boost::math::find_location;
и затем использовать функцию find_location для поиска safe_mean, & построить новое нормальное распределение под названием Goodpacks.
double safe_mean = find_location<normal>(minimum_weight, under_fraction, standard_deviation);
normal good_packs(safe_mean, standard_deviation);
с тем же подтверждением, что и раньше:
cout << "Setting the packer to " << nominal_mean << " will mean that "
<< "fraction of packs >= " << minimum_weight
<< " is " << cdf(complement(good_packs, minimum_weight)) << endl;
Изучив распределение веса большого количества упаковок, мы можем решить, что, в конце концов, предположение о нормальном распределении не совсем оправдано. Мы можем обнаружить, что подгонка лучше дляраспределения Коши. Это распределение имеет более широкие «крылья», так что, хотя большинство значений ближе к среднему значению, чем нормальное, есть также больше значений, чем «нормальное», которые лежат дальше от среднего, чем нормальное.
Это может произойти потому, что большая, чем обычно, часть мяса либо включена, либо исключена.
Сначала мы создаемCauchy Distributionс исходным средним и стандартным отклонением и оцениваем фракцию, которая лежит ниже нашей минимальной спецификации веса.
cauchy cpacks(mean, standard_deviation);
cout << "Cauchy Setting the packer to " << mean << " will mean that "
<< "fraction of packs >= " << minimum_weight
<< " is " << cdf(complement(cpacks, minimum_weight)) << endl;
Обратите внимание, что гораздо меньше упаковок соответствует спецификации, только 75% вместо 95%. Теперь мы можем повторить местоположение find_location, используя распределение cauchy в качестве параметра шаблона, вместо обычного, используемого выше.
double lc = find_location<cauchy>(minimum_weight, under_fraction, standard_deviation);
cout << "find_location<cauchy>(minimum_weight, over fraction, standard_deviation); " << lc << endl;
Обратите внимание, что настройка Safe_mean должна быть намного выше, 3.53138 вместо 3.06449, поэтому мы получим гораздо меньшую прибыль.
И еще раз подтвердите, что спецификация соответствия фракции соответствует ожиданиям.
cauchy goodcpacks(lc, standard_deviation);
cout << "Cauchy Setting the packer to " << lc << " will mean that "
<< "fraction of packs >= " << minimum_weight
<< " is " << cdf(complement(goodcpacks, minimum_weight)) << endl;
Наконец, мы могли бы оценить эффект гораздо более жесткой спецификации, что 99% упаковок соответствовали спецификации.
cout << "Cauchy Setting the packer to "
<< find_location<cauchy>(minimum_weight, 0.99, standard_deviation)
<< " will mean that "
<< "fraction of packs >= " << minimum_weight
<< " is " << cdf(complement(goodcpacks, minimum_weight)) << endl;
Установка упаковщика на 3.13263 будет означать, что доля упаковок >= 2.9 составляет 0,99, но более чем удвоит среднюю потерю от 0,0644 до 0,133 кг на упаковку.
Конечно, этот расчет не ограничивается упаковками мяса, он относится к раздаче чего-либо, а также к «виртуальному» материалу, как и любое измерение.
Единственное предостережение заключается в том, что расчет предполагает, что стандартное отклонение (шкала) известно с достаточно низкой неопределенностью, что не так легко обеспечить на практике. И что распределение хорошо определено,нормальное распределениеилираспределение Кошиили какое-то другое.
Если вы просто распределяете очень большое количество упаковок, то можно измерить вес сотен или тысяч упаковок. При здоровых "градусах свободы" доверительные интервалы для стандартного отклонения не слишком широки, как правило, около + и - 10% для сотен наблюдений.
Для других приложений, где сделать много наблюдений сложнее или дороже, доверительные интервалы удручающе широки.
См.Интервалы доверия по стандартному отклонениюдля работающего примераchi_square_std_dev_test.cppоценки этих интервалов.
В качестве альтернативы мы могли бы инвестировать в лучший (более точный) упаковщик (или измерительное устройство) с более низким стандартным отклонением или масштабом.
Это может стоить дороже, но уменьшит сумму, которую мы должны «отдать», чтобы соответствовать спецификации.
Чтобы оценить, насколько лучше (насколько меньше стандартное отклонение) это должно быть, нам нужно получить 5% квантиль, чтобы быть расположенным на пределе ниже веса, 2,9.
double p = 0.05;
cout << "Quantile of " << p << " = " << quantile(packs, p)
<< ", mean = " << packs.mean() << ", sd = " << packs.standard_deviation() << endl;
Количественное значение 0,05 = 2.83551, среднее = 3, sd = 0,1
При текущем упаковщике (среднее значение = 3, sd = 0,1) 5% квантиле составляет 2,8551 кг, что немного ниже нашей цели в 2,9 кг. Поэтому мы знаем, что стандартное отклонение должно быть меньше.
Начнем с предположения, что ее (сейчас 0,1) нужно сократить вдвое, до стандартного отклонения в 0,05 кг.
normal pack05(mean, 0.05);
cout << "Quantile of " << p << " = " << quantile(pack05, p)
<< ", mean = " << pack05.mean() << ", sd = " << pack05.standard_deviation() << endl;
cout <<"Fraction of packs >= " << minimum_weight << " with a mean of " << mean
<< " and standard deviation of " << pack05.standard_deviation()
<< " is " << cdf(complement(pack05, minimum_weight)) << endl;
Так что 0,05 было довольно хорошей догадкой, но мы немного превысили цель 2,9, так что стандартное отклонение может быть немного больше. Так что мы могли бы сделать еще несколько догадок, чтобы приблизиться, скажем, увеличив стандартное отклонение до 0,06 кг, построив еще одно новое распределение под названием Pack06.
normal pack06(mean, 0.06);
cout << "Quantile of " << p << " = " << quantile(pack06, p)
<< ", mean = " << pack06.mean() << ", sd = " << pack06.standard_deviation() << endl;
cout <<"Fraction of packs >= " << minimum_weight << " with a mean of " << mean
<< " and standard deviation of " << pack06.standard_deviation()
<< " is " << cdf(complement(pack06, minimum_weight)) << endl;
Теперь мы действительно приближаемся, но для правильного выполнения работы нам, возможно, потребуется использовать метод поиска корней, например, инструменты, предоставленные и используемые в другом месте, в Math Toolkit, см.
Но в этом (нормальном) случае распределения мы можем и должны быть еще умнее и произвести прямой расчет.
Наш необходимый предел - минимальный вес = 2,9 кг, часто называемый случайной вариацией z. Для стандартного нормального распределения вероятность p = N((minimum_weight - среднее)/sd).
Мы хотим найти стандартное отклонение, которое было бы необходимо для достижения этого предела, так что p th quantile находится в точке z (минимальный вес). В этом случае квантиль 0,05 (5%) имеет вес упаковки 2,9 кг, в то время как средний вес составляет 3 кг, гарантируя, что 0,95 (95%) упаковок превышают минимальный вес.
Перегруппировавшись, можно напрямую рассчитать требуемое стандартное отклонение:
normal N01;
p = 0.05;
double qp = quantile(N01, p);
double sd95 = (minimum_weight - mean) / qp;
cout << "For the "<< p << "th quantile to be located at "
<< minimum_weight << ", would need a standard deviation of " << sd95 << endl;
Теперь мы можем построить новый (нормальный) пакет 95 распределения для «лучшего» упаковщика и проверить, что наш дистрибутив будет соответствовать спецификации.
normal pack95(mean, sd95);
cout <<"Fraction of packs >= " << minimum_weight << " with a mean of " << mean
<< " and standard deviation of " << pack95.standard_deviation()
<< " is " << cdf(complement(pack95, minimum_weight)) << endl;
Это вычисление обобщается в свободной функции find_scale, как показано ниже, давая такое же стандартное отклонение.
double ss = find_scale<normal>(minimum_weight, under_fraction, packs.mean());
cout << "find_scale<normal>(minimum_weight, under_fraction, packs.mean()); " << ss << endl;
Если бы мы определили over_fraction или процент, который должен пройти спецификацию
double over_fraction = 0.95;
(неправильно написанный)
double sso = find_scale<normal>(minimum_weight, over_fraction, packs.mean());
С политикой дефолта мы получим сообщение, подобное
Message from thrown exception was:
Error in function boost::math::find_scale<Dist, Policy>(double, double, double, Policy):
Computed scale (-0.060795683191176959) is <= 0! Was the complement intended?
Но это вернетотрицательноестандартное отклонение — очевидно, невозможное. Вероятность должна быть 1 - over_fraction, а не over_fraction, таким образом:
double ss1o = find_scale<normal>(minimum_weight, 1 - over_fraction, packs.mean());
cout << "find_scale<normal>(minimum_weight, under_fraction, packs.mean()); " << ss1o << endl;
Но обратите внимание, что использование 1 - over_fraction приведет к потере точности, особенно если over_fraction был близок к единству. (См.Почему это так?)В этом (очень распространенном) случае мы должны вместо этого использоватьдополнения, давая наиболее точный результат.
double ssc = find_scale<normal>(complement(minimum_weight, over_fraction, packs.mean()));
cout << "find_scale<normal>(complement(minimum_weight, over_fraction, packs.mean())); " << ssc << endl;
Обратите внимание, что наша догадка 0,06 была близка к точному значению 0,060795683191176959.
Мы можем подтвердить наш прогноз таким образом:
normal pack95c(mean, ssc);
cout <<"Fraction of packs >= " << minimum_weight << " with a mean of " << mean
<< " and standard deviation of " << pack95c.standard_deviation()
<< " is " << cdf(complement(pack95c, minimum_weight)) << endl;
Обратите внимание, что эти два обманчиво простых вопроса:
- Мы переполняем, чтобы убедиться, что мы удовлетворяем минимальным требованиям (или недополняем, чтобы избежать передозировки)?
и/или
На самом деле они чрезвычайно распространены.
Вес говядины может быть заменен измерением более или менее чего-либо, от содержания лекарственных таблеток, десантных ракетных стрельб Аполлона, доз рентгеновского лечения.
Шкала может быть вариацией в распределении или неопределенностью в измерении.
См.find_mean_and_sd_normal.cppдля полного исходного кода & добавлен выход программы.