C语言随机加权,c ++ - 加权随机数

c ++ - 加权随机数

我正在尝试实施加权随机数。 我现在只是把头靠在墙上,无法解决这个问题。

在我的项目(德州扑克手牌范围,主观全权证券分析)中,我正在使用Boost的随机函数。 所以,假设我想选择1到3之间的随机数(所以要么是1,2或3)。 Boost的mersenne twister发电机就像这样的魅力。 但是,我希望选择加权,例如:

1 (weight: 90)

2 (weight: 56)

3 (weight: 4)

Boost是否具有某种功能?

nhaa123 asked 2019-03-31T12:34:04Z

7个解决方案

136 votes

有一个简单的随机选择项目的算法,其中项目具有单独的权重:

1)计算所有权重的总和

2)选择0或更大且小于权重之和的随机数

3)一次一个地查看项目,从随机数中减去它们的重量,直到得到随机数小于项目重量的项目为止

伪代码说明了这一点:

int sum_of_weight = 0;

for(int i=0; i

sum_of_weight += choice_weight[i];

}

int rnd = random(sum_of_weight);

for(int i=0; i

if(rnd < choice_weight[i])

return i;

rnd -= choice_weight[i];

}

assert(!"should never get here");

这应该很容易适应你的增压容器等。

如果您的权重很少改变,但您经常随机选择一个,并且只要您的容器存储指向对象的指针或长度超过几十个项目(基本上,您必须剖析以了解这是否有帮助或阻碍) ,然后有一个优化:

通过在每个项目中存储累积权重总和,您可以使用二分搜索来选择与拾取权重相对应的项目。

如果您不知道列表中的项目数,那么有一个非常简洁的算法,称为水库采样,可以调整为加权。

Will answered 2019-03-31T12:35:21Z

43 votes

更新了旧问题的答案。 您可以使用std :: lib在C ++ 11中轻松完成此操作:

#include

#include

#include

#include

#include

#include

int main()

{

// Set up distribution

double interval[] = {1, 2, 3, 4};

double weights[] = { .90, .56, .04};

std::piecewise_constant_distribution<> dist(std::begin(interval),

std::end(interval),

std::begin(weights));

// Choose generator

std::mt19937 gen(std::time(0)); // seed as wanted

// Demonstrate with N randomly generated numbers

const unsigned N = 1000000;

// Collect number of times each random number is generated

double avg[std::extent::value] = {0};

for (unsigned i = 0; i < N; ++i)

{

// Generate random number using gen, distributed according to dist

unsigned r = static_cast(dist(gen));

// Sanity check

assert(interval[0] <= r && r <= *(std::end(interval)-2));

// Save r for statistical test of distribution

avg[r - 1]++;

}

// Compute averages for distribution

for (double* i = std::begin(avg); i < std::end(avg); ++i)

*i /= N;

// Display distribution

for (unsigned i = 1; i <= std::extent::value; ++i)

std::cout << "avg[" << i << "] = " << avg[i-1] << '\n';

}

我的系统输出:

avg[1] = 0.600115

avg[2] = 0.373341

avg[3] = 0.026544

请注意,上面的大部分代码都专门用于显示和分析输出。 实际生成只是几行代码。 输出表明已获得所请求的“概率”。 您必须将请求的输出除以1.5,因为这是请求加起来的。

Howard Hinnant answered 2019-03-31T12:36:02Z

7 votes

当我需要加权数字时我所做的是使用随机数作为重量。

例如:我需要使用以下权重生成1到3的随机数:

随机数的10%可以是1

随机数的30%可能是2

60%的随机数可能是3

然后我用:

weight = rand() % 10;

switch( weight ) {

case 0:

randomNumber = 1;

break;

case 1:

case 2:

case 3:

randomNumber = 2;

break;

case 4:

case 5:

case 6:

case 7:

case 8:

case 9:

randomNumber = 3;

break;

}

有了它,随机它有10%的概率为1,30%为2和60%为3。

您可以根据需要使用它。

希望我能帮助你,祝你好运!

Chirry answered 2019-03-31T12:37:29Z

7 votes

如果你的权重变化比绘制的慢,C ++ 11 discrete_distribution将是最简单的:

#include

#include

std::vector weights{90,56,4};

std::discrete_distribution dist(std::begin(weights), std::end(weights));

std::mt19937 gen;

gen.seed(time(0));//if you want different results from different runs

int N = 100000;

std::vector samples(N);

for(auto & i: samples)

i = dist(gen);

//do something with your samples...

但请注意,c ++ 11 /usr/include/c++/5/bits/random.tcc计算初始化时的所有累积和。 通常,您需要这样做,因为它会加快一次O(N)成本的采样时间。 但是对于快速变化的分布,它将导致繁重的计算(和存储器)成本。 例如,如果权重表示有多少项目,并且每次绘制一个项目,则删除它,您可能需要自定义算法。

Will的回答[https://stackoverflow.com/a/1761646/837451]避免了这种开销,但是比C ++ 11更慢,因为它不能使用二进制搜索。

要看到它执行此操作,您可以看到相关的行(我的Ubuntu 16.04 + GCC 5.3安装上的/usr/include/c++/5/bits/random.tcc):

template

void

discrete_distribution<_inttype>::param_type::

_M_initialize()

{

if (_M_prob.size() < 2)

{

_M_prob.clear();

return;

}

const double __sum = std::accumulate(_M_prob.begin(),

_M_prob.end(), 0.0);

// Now normalize the probabilites.

__detail::__normalize(_M_prob.begin(), _M_prob.end(), _M_prob.begin(),

__sum);

// Accumulate partial sums.

_M_cp.reserve(_M_prob.size());

std::partial_sum(_M_prob.begin(), _M_prob.end(),

std::back_inserter(_M_cp));

// Make sure the last cumulative probability is one.

_M_cp[_M_cp.size() - 1] = 1.0;

}

mmdanziger answered 2019-03-31T12:38:18Z

3 votes

构建可以拾取的所有项目的包(或std :: vector)。

确保每个项目的数量与您的权重成比例。

例:

1 60%

2 35%

3 5%

所以有一个包含100件物品的袋子,60件1件,35件2件和5件3件。

现在随机排序包(std :: random_shuffle)

从袋子中依次挑选元素,直到它为空。

一旦空,重新随机化袋子并重新开始。

Martin York answered 2019-03-31T12:39:51Z

0 votes

在[0,1)上选择一个随机数,它应该是增强RNG的默认运算符()。 选择具有累积概率密度函数&gt; =该数字的项目:

template

It choose_p(It begin,It end,P const& p)

{

if (begin==end) return end;

double sum=0.;

for (It i=begin;i!=end;++i)

sum+=p(*i);

double choice=sum*random01();

for (It i=begin;;) {

choice -= p(*i);

It r=i;

++i;

if (choice<0 || i==end) return r;

}

return begin; //unreachable

}

其中random01()返回double&gt; = 0且&lt; 1。 注意,上面不要求概率总和为1; 它为你规范化了。

p只是一个为集合中的项目[开始,结束]分配概率的函数。 如果您只有一系列概率,则可以省略它(或使用标识)。

Jonathan Graehl answered 2019-03-31T12:40:35Z

-2 votes

我已经实现了几个简单的加权随机算法。

Leonid Ganeline answered 2019-03-31T12:41:03Z

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 C 语言实现随机一个加权图的建立和 Prim 算法求最小生成树的代码: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <limits.h> #define MAX_VERTICES 100 #define INF INT_MAX int n; // 图的顶点数 int dist[MAX_VERTICES]; // 存放从已经包含在最小生成树中的顶点到其它顶点的最短距离 int visited[MAX_VERTICES]; // 标记顶点是否已经包含在最小生成树中 int graph[MAX_VERTICES][MAX_VERTICES]; // 存放图的邻接矩阵 void prim(int start) { int i, j, k; int min; int u; // 初始化 for (i = 0; i < n; i++) { dist[i] = INF; visited[i] = 0; } dist[start] = 0; for (i = 0; i < n; i++) { min = INF; for (j = 0; j < n; j++) { if (!visited[j] && dist[j] < min) { min = dist[j]; u = j; } } visited[u] = 1; for (k = 0; k < n; k++) { if (!visited[k] && graph[u][k] < dist[k]) { dist[k] = graph[u][k]; } } } } int main() { int i, j; // 读入图的顶点数和边数 int m; printf("Enter the number of vertices: "); scanf("%d", &n); printf("Enter the number of edges: "); scanf("%d", &m); // 初始化随机数种子 srand(time(NULL)); // 随机生成边并更新邻接矩阵 int u, v, w; for (i = 0; i < m; i++) { u = rand() % n; v = rand() % n; w = rand() % 100 + 1; if (u != v && graph[u][v] == INF) { graph[u][v] = w; graph[v][u] = w; } else { i--; } } // 调用 Prim 算法求最小生成树 prim(0); // 输出最小生成树的边权和 int sum = 0; for (i = 0; i < n; i++) { sum += dist[i]; } printf("The weight of the minimum spanning tree is %d.\n", sum); return 0; } ``` 在上述代码中,我们首先读入图的顶点数和边数,然后初始化随机数种子。接着,我们使用 `rand()` 函数随机生成边并更新邻接矩阵,其中边的权值为 1 到 100 之间的随机整数。由于是无向图,所以需要更新两个顶点之间的邻接关系。如果生成的边已经存在或者是自环边,则需要重新生成,直到生成一条新的边为止。最后,调用 `prim()` 函数求最小生成树,并输出最小生成树的边权和。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值