Алгоритм вычисления вероятности суммы результатов

Алгоритм, о котором я говорю, позволит вам представить его с x количеством элементов, каждый из которых имеет диапазон от a до b с результатом y. Я хотел бы иметь алгоритм, который при представлении со значениями, как описано, вывел бы возможность того, что это произойдет.

Например, на двоих умирают. Так как я их уже знаю (из-за возможных низких результатов). Он мог бы рассказать вам о каждой из возможностей.

Настройка будет что-то вроде. х = 2 а = 1 б = 6. Если бы вы хотели знать, что в результате вы получите 2, тогда оно просто выплюнет 1/36 (или это значение с плавающей запятой). Если вы введете 7 в качестве общей суммы, она скажет вам 6.

Итак, мой вопрос, есть ли простой способ реализовать такую вещь с помощью алгоритма, который уже написан. Или нужно пройти каждую итерацию каждого элемента, чтобы получить общее количество комбинаций для каждого значения.

Точная формула также даст вам комбинации, чтобы получить каждое из значений от 1 до 12.

Так что это даст вам массив распределения с каждой комбинацией в каждом из индексов. Если это 0-12. Тогда 0 будет иметь 0, 1 будет иметь 0, а 2 будет иметь 1.

Я чувствую, что это тот тип проблем, с которыми кто-то сталкивался и хотел поработать, а алгоритм уже готов. Если у кого-то есть простой способ сделать это, кроме простого обхода всех возможных значений, было бы здорово.

Я понятия не имею, почему я хочу решить эту проблему, но по какой-то причине сегодня у меня просто возникло желание решить ее. И так как я гуглил и использовал вольфрам альфа, вместе с тем пробовал сам. Я думаю, пришло время признать поражение и спросить сообщество.

Я бы хотел, чтобы алгоритм был на c, или, может быть, на PHP (хотя я бы предпочел, чтобы он не был, так как он намного медленнее). Причина для c просто потому, что я хочу сырую скорость, и я не хочу иметь дело с классами или объектами.

Псевдокод или C - лучший способ показать ваш алгоритм.

Редактировать:

Кроме того, если я обидел человека с буквой "b" в его имени из-за математики, извините. Так как я не хотел обидеть, но я хотел бы просто сказать, что я не понимаю. Но ответ мог бы остаться там, так как я уверен, что есть люди, которые могут прийти к этому вопросу и понять математику, стоящую за ним.

Также я не могу решить, каким способом я хочу это закодировать. Я думаю, что попробую использовать оба, а затем решу, какой из них мне больше нравится видеть/использовать внутри моей маленькой библиотеки.

Последнее, что я забыл сказать, это то, что исчисление примерно четыре продолжается пять лет назад. Мое понимание вероятности, статистики и случайности основано на моем собственном обучении через просмотр кода/чтение википедии/чтение книг.

Если кому-то интересно, что вызвало этот вопрос. У меня была книга, которую я отложил для чтения, под названием "Пьяная прогулка", и затем, когда я произнес XKCD 904, я решил, что пришло время наконец-то приступить к ее чтению. Затем, две ночи назад, когда я ложился спать, я размышлял, как решить этот вопрос с помощью простого алгоритма, и смог придумать один.

Мое понимание кодирования кода происходит от работы с другими программами, наблюдения за тем, что произошло, когда я что-то сломал, и затем пробовал свои собственные вещи, одновременно просматривая документацию для встроенных функций. Я понимаю большие нотации O при чтении по Википедии (насколько это возможно), и псевдокод был потому, что он так похож на python. Я сам не могу написать псевдокод (или говорит учитель в колледже). Я продолжал получать заметки типа "сделай его менее похожим на реальный код, сделай его больше похожим на псевдокод". Эта вещь не изменилась.

Редактировать 2: если кто-то ищет этот вопрос, он просто хочет получить код. Я включил это ниже. Он лицензирован под LGPLv3, так как я уверен, что существуют эквиваленты этого кода с закрытым исходным кодом.

Он должен быть достаточно переносимым, так как полностью написан на c. Если кто-то хотел превратить его в расширение на любом из различных языков, написанных на c, это должно занять очень мало усилий. Я выбрал "пометить" первый ответ, связанный с "Спросите доктора Матха", так как это была реализация, которую я использовал для этого вопроса.

Первое имя файла "sum_probability.c"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

/*!
*    file_name: sum_probability.c
*    
*    Set of functions to calculate the probabilty of n number of items adding up to s
*    with sides x. The question that this program relates to can be found at the url of
*    http://stackoverflow.com/info/6394120/
*    
*     Copyright 2011-2019, Macarthur Inbody
*    
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the Lesser GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the Lesser GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*     
*   2011-06-20 06:03:57 PM -0400
*    
*   These functions work by any input that is provided. For a function demonstrating it.
*   Please look at the second source file at the post of the question on stack overflow.
*   It also includes an answer for implenting it using recursion if that is your favored
*   way of doing it. I personally do not feel comfortable working with recursion so that is
*   why I went with the implementation that I have included.
*
*/

/*
* The following functions implement falling factorials so that we can
* do binomial coefficients more quickly.
* Via the following formula.
*
*   K
*  PROD    (n-(k-i))/i
*   i=1;
*
*/

//unsigned int return
unsigned int m_product_c( int k,  int n){
    int i=1;
    float result=1;
    for(i=1;i<=k;++i){
        result=((n-(k-i))/i)*result;
    }
    return result;
}

//float return
float m_product_cf(float n, float k){
    int i=1;
    float result=1;
    for(i=1;i<=k;++i){
        result=((n-(k-i))/i)*result;
    }
    return result;
}


/*
* The following functions calculates the probability of n items with x sides
* that add up to a value of s. The formula for this is included below.
*
* The formula comes from. http://mathforum.org/library/drmath/view/52207.html
*
*s=sum
*n=number of items
*x=sides
*(s-n)/x
* SUM  (-1)^k * C(n,k) * C(s-x*k-1,n-1)
* k=0
*
*/

float chance_calc_single(float min, float max, float amount, float desired_result){
    float range=(max-min)+1;
    float series=ceil((desired_result-amount)/range);
    float i;
    --amount;
    float chances=0.0;
    for(i=0;i<=series;++i){
        chances=pow((-1),i)*m_product_cf(amount,i)*m_product_cf(desired_result-(range*i)-1,amount)+chances;
    }
    return chances;
}

И вот файл, который показывает реализацию, как я сказал в предыдущем файле.

#include "sum_probability.c"

/*
* 
* file_name:test.c
*
* Function showing off the algorithms working. User provides input via a cli
* And it will give you the final result.
*
*/
int main(void){
        int amount,min,max,desired_results;
        printf("%s","Please enter the amount of items.\n");
        scanf("%i",&amount);
        printf("%s","Please enter the minimum value allowed.\n");
        scanf("%i",&min);
        printf("%s","Please enter the maximum value allowed.\n");
        scanf("%i",&max);
        printf("%s","Please enter the value you wish to have them add up to. \n");
        scanf("%i",&desired_results);
        printf("The total chances for %i is %f.\n", desired_results, chance_calc_single(min, max, amount, desired_results));
}

Ответ 1

Прежде всего, вам не нужно беспокоиться о диапазоне от a до b. Вы можете просто вычесть a*x из y и сделать вид, что диапазон от 0 до b-a. (Поскольку каждый элемент вносит по меньшей мере a в сумму... Таким образом, вы можете вычесть это a один раз для каждого из ваших элементов x.)

Во-вторых, обратите внимание, что то, что вы действительно пытаетесь сделать, - это количество способов достижения определенной суммы. Вероятность - это просто счет, деленный на простой экспоненциальный (b-a+1)^x.

Эта проблема была рассмотрена "Ask Dr. Math" около десяти лет назад:

http://mathforum.org/library/drmath/view/52207.html

Его формулировка предполагает, что кости имеют номер от 1 до X, поэтому, чтобы использовать его ответ, вы, вероятно, захотите изменить свой диапазон на a-1 (а не a), чтобы преобразовать его в эту форму.

Его деривация использует генерирующие функции, которые, как мне кажется, заслуживают небольшого объяснения. Идея состоит в том, чтобы определить многочлен f(z) такой, что коэффициент на z^n - это число способов прокатки n. Например, для одной 6-сторонней матрицы это производящая функция:

z + z^2 + z^3 + z^4 + z^5 + z^6

... потому что есть один способ прокатки каждого числа от 1 до 6 и нулевые способы прокатки всего остального.

Теперь, если у вас есть две производящие функции g(z) и h(z) для двух наборов кубиков, получается, что производящая функция объединения этих множеств является просто произведением g и h. (Старайтесь на операцию "умножить два полинома" на некоторое время, чтобы убедить себя, что это правда.) Например, для двух кубиков мы можем просто скомпоновать указанное выше выражение, чтобы получить:

z^2 + 2z^3 + 3z^4 +4z^5 + 5z^6 + 6z^7 + 5z^8 + 4z^9 + 3z^10 + 2z^11 + z^12

Обратите внимание, как мы можем прочитать количество комбинаций непосредственно из коэффициентов: 1 способ получить 2 (1*z^2), 6 способов получить 7 (6*z^7) и т.д.

Куб выражения даст нам производящую функцию для трех кубиков; четвертая сила, четыре кости; и т.д.

Сила этой формулировки возникает, когда вы пишете производящие функции в замкнутой форме, размножаетесь, а затем разворачиваете их снова, используя Биномиальная теорема. Я откладываю до объяснения доктора Мата подробности.

Ответ 2

Скажем, что f(a, b, n, x) представляет количество способов, которыми вы можете выбрать n чисел между a и b, которые суммируются до x.

Затем обратите внимание, что:

f(a, b, n, x) = f(0, b-a, n, x-n*a)

В самом деле, просто возьмите один способ достижения суммы x и из каждого из n чисел вычесть a, тогда общая сумма станет x - n*a, и каждая из них будет находиться между 0 и b-a.

Таким образом, достаточно написать код, чтобы найти f(0, m, n, x).

Теперь обратите внимание, что все способы достижения цели, такие, что последнее число - c:

f(0, m, n-1, x-c)

В самом деле, мы оставили n-1 чисел и хотим, чтобы общая сумма была x-c. Тогда мы имеем рекуррентную формулу:

f(0,m,n,x) = f(0,m,n-1,x) + f(0,m,n-1,x-1) + ... + f(0,m,n-1,x-m)

где слагаемые справа соответствуют последнему числу, равному 0, 1,..., m

Теперь вы можете реализовать это с помощью рекурсии, но это будет слишком медленно.

Однако есть трюк, называемый memoized recursion, т.е. вы сохраняете результат функции, так что вам не нужно ее снова вычислять (для тех же аргументов).

Памятная рекурсия будет иметь сложность O(m * n), так как количество различных входных параметров, которые вам нужно вычислить и сохранить.

Как только вы вычислили счет, вам нужно разделить на общее количество posiblities, что (m + 1) * n, чтобы получить окончательную вероятность.

Ответ 3

Чтобы получить все возможности, вы можете сделать карту значений:

for (i=a to b) {
 for (j=a to b) {
  map.put(i+j, 1+map.get(i+j))
 }
}

Для более эффективного способа подсчета сумм вы можете использовать шаблон 6 7, 5 6, 4 5, 3 4, 2 3, 1 2.

Шаблон выполняется для n x n сетки, будет n (n + 1) 's, с одной меньшей возможностью для суммы 1 больше или меньше.

Это будет учитывать возможности, например, Count (6, 1/2/3/4/5/6) даст возможности для сумм кубиков.

import math
def Count(poss,sumto):
  return poss - math.fabs(sumto-(poss+1));

Изменить: В C это будет:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>;

int count(int poss, int sumto)
{
  return poss - abs(sumto-(poss+1));
}

int main(int argc, char** argv) {
    printf("With two dice,\n");
    int i;
    for (i=1; i<= 13; i++)
    {
        printf("%d ways to sum to %d\n",count(6,i),i);
    }
    return (EXIT_SUCCESS);
}

дает:

With two dice,
0 ways to sum to 1
1 ways to sum to 2
2 ways to sum to 3
3 ways to sum to 4
4 ways to sum to 5
5 ways to sum to 6
6 ways to sum to 7
5 ways to sum to 8
4 ways to sum to 9
3 ways to sum to 10
2 ways to sum to 11
1 ways to sum to 12
0 ways to sum to 13

Ответ 4

Теория чисел, статистика и комбинаторика заставляют вас полагать, что для получения числового значения вероятности события - ну, вы должны знать 2 вещи:

  • количество возможных результатов
  • в пределах совокупности итоговых результатов, сколько равно результат "y", значение вероятности которого вы ищете.

В псевдокоде:

numPossibleOutcomes = calcNumOutcomes(x, a, b);
numSpecificOutcomes = calcSpecificOutcome(y);
probabilityOfOutcome = numSpecificOutcomes / numPossibleOutcomes;

Затем просто скопируйте выше 2 функции, которые должны быть легкими.