Алгоритм расчета вероятности суммы полученных результатов - PullRequest
10 голосов
/ 18 июня 2011

Алгоритм, о котором я говорю, позволил бы вам представить его с 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» в его имени из-за математики, извините.Так как я не хотел обидеть, но хотел просто заявить, что я не понял этого.Но ответ мог бы остаться там, так как я уверен, что есть люди, которые могут прийти к этому вопросу и понять математику, стоящую за ним.

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

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

Если кому-то интересно, что вызвало этот вопрос.У меня была книга, которую я откладывал на чтение, под названием The Drunkards Walk , а потом, когда я произнес 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/questions/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));
}

Ответы [ 4 ]

12 голосов
/ 18 июня 2011

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

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

Эта проблема была рассмотрена «Спросите доктора Матха» около десяти лет назад:

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 голосов
/ 18 июня 2011

Скажем, что 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

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

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

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

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

0 голосов
/ 18 июня 2011

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

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

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

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

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

0 голосов
/ 18 июня 2011

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

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 два.

Схема справедлива для сетки nxn, будет n (n + 1) с одной меньшей вероятностью для суммы 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
...