Existe um algoritmo simples para escolher um item aleatoriamente, onde os itens têm pesos individuais:
1) calcular a soma de todos os pesos
2) escolha um número aleatório que seja 0 ou maior e menor que a soma dos pesos
3) analise os itens um de cada vez, subtraindo o peso do seu número aleatório, até obter o item em que o número aleatório é menor que o peso do item
Pseudo-código que ilustra isso:
int sum_of_weight = 0;
for(int i=0; i<num_choices; i++) {
sum_of_weight += choice_weight[i];
}
int rnd = random(sum_of_weight);
for(int i=0; i<num_choices; i++) {
if(rnd < choice_weight[i])
return i;
rnd -= choice_weight[i];
}
assert(!"should never get here");
Isso deve ser simples para se adaptar aos seus recipientes de impulso e tal.
Se seus pesos são raramente alterados, mas você costuma escolher um ao acaso, e desde que seu contêiner esteja armazenando ponteiros para os objetos ou tenha mais do que algumas dezenas de itens (basicamente, você tem que fazer um perfil para saber se isso ajuda ou atrapalha) , então há uma otimização:
Ao armazenar a soma do peso cumulativo em cada item, você pode usar uma pesquisa binária para escolher o item correspondente ao peso escolhido.
Se você não souber o número de itens na lista, então existe um algoritmo muito bom chamado amostragem de reservatório que pode ser adaptado para ser ponderado.
A Monte Carlo method called Russian roulette is used to choose one of these actions
aparece em baldes ao pesquisar no Google. "algoritmo de roleta russa". Você pode argumentar que todas essas pessoas têm o nome errado.Resposta atualizada para uma pergunta antiga. Você pode fazer isso facilmente em C ++ 11 com apenas std :: lib:
Saída em meu sistema:
Observe que a maior parte do código acima é dedicado apenas a exibir e analisar a saída. A geração real é apenas algumas linhas de código. A saída demonstra que as "probabilidades" solicitadas foram obtidas. Você tem que dividir a saída solicitada por 1,5, pois é isso que as solicitações somam.
fonte
std::discrete_distribution
vez destd::piecewise_constant_distribution
teria sido ainda melhor.Se seus pesos mudam mais lentamente do que são desenhados, C ++ 11
discrete_distribution
será o mais fácil:Observe, entretanto, que o c ++ 11
discrete_distribution
calcula todas as somas cumulativas na inicialização. Normalmente, você deseja isso porque acelera o tempo de amostragem para um custo O (N) único. Mas, para uma distribuição que muda rapidamente, isso incorrerá em um alto custo de cálculo (e memória). Por exemplo, se os pesos representavam quantos itens existem e cada vez que você desenha um e o remove, provavelmente desejará um algoritmo personalizado.A resposta de Will https://stackoverflow.com/a/1761646/837451 evita essa sobrecarga, mas será mais lento para desenhar do que o C ++ 11 porque não pode usar a pesquisa binária.
Para ver se ele faz isso, você pode ver as linhas relevantes (
/usr/include/c++/5/bits/random.tcc
na minha instalação do Ubuntu 16.04 + GCC 5.3):fonte
O que eu faço quando preciso pesar números é usar um número aleatório para o peso.
Por exemplo: preciso que gere números aleatórios de 1 a 3 com os seguintes pesos:
Então eu uso:
Com isso, aleatoriamente tem que 10% das probabilidades sejam 1, 30% sejam 2 e 60% sejam 3.
Você pode brincar com ele conforme suas necessidades.
Espero poder ajudar você, boa sorte!
fonte
Construa uma bolsa (ou std :: vector) com todos os itens que podem ser coletados.
Certifique-se de que o número de cada item seja proporcional à sua ponderação.
Exemplo:
Portanto, tenha uma bolsa com 100 itens com 60 1's, 35 2's e 5 3's.
Agora, classifique o saco aleatoriamente (std :: random_shuffle)
Escolha os elementos do saco sequencialmente até que esteja vazio.
Uma vez vazio, re-randomize o saco e comece novamente.
fonte
1,2,2
produzindo 1 1/3 do tempo e 2 2/3. Randomize o array, escolha o primeiro, digamos 2, agora o próximo elemento que você escolhe segue a distribuição de 1 1/2 do tempo e 2 1/2 do tempo. Savvy?Escolha um número aleatório em [0,1), que deve ser o operador padrão () para um RNG de reforço. Escolha o item com função de densidade de probabilidade cumulativa> = esse número:
Onde random01 () retorna um duplo> = 0 e <1. Observe que o exposto acima não exige que as probabilidades sejam 1; isso os normaliza para você.
p é apenas uma função que atribui uma probabilidade a um item da coleção [início, fim). Você pode omiti-lo (ou usar uma identidade) se tiver apenas uma sequência de probabilidades.
fonte
Implementei vários algoritmos aleatórios ponderados simples .
fonte