当前位置: 首页 > article >正文

(leetcode算法题)528. 按权重随机选择

测试阶段将会反复调用多次成员函数,期望的结果是调用多次后满足:

第 i 个下标被返回的概率是 w[i] / Σw[j],

可以利用rand() % Σw[j] 可以等概率的得到[0, Σw[j] - 1] 这个区间内的整数,且取到其中任意一个整数的概率是 1 / Σw[j]。

那么可以很容易的想到,将一个区间分成Σw[j] 段,取到每段的概率都是1 / Σw[j],

对于第 i 个下标来说,将区间中的 w[i] 个段分配给这第 i 个下标,那么取到第 i 个下标对应的那些段的概率就是w[i] / Σw[j],

Example.

假设 权重数组为[2, 5, 3]

那么可以生成如下示意图,总共有10段,用rand() % 10 获得[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]中的一个随机数,获得其中每个数的概率都是1 / 10,

则取到红色的概率是2 / 10,取到绿色的概率是5 / 10,取到黄色的概率是 3 / 10

那么用代码怎么实现这一点?下面分为两部分伪代码分别说明求前缀和 和 查找pos这两步

求前缀和:

思想:使用前缀和数组让[0, 1]对应红色,[2, 6]对应绿色,[7, 9]对应黄色

[2, 5, 3]的前缀和数组为[2, 7, 10] 2就是红色的末尾,7就是绿色的末尾,9就是黄色的末尾

记[2, 7, 10]为presum

伪代码:

for(i 从 1 到 n - 1){ nums[i] = nums[i - 1] + nums[i]; }

好了好了,上代码!求前缀和数组代码如下:

partial_sum(_weight.begin(), _weight.end(), _weight.begin());

对于partial_sum接口的说明

请看链接std::partial_sum - cppreference.com

template< class InputIt, class OutputIt >

OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first);

好了好了,那怎么通过x找到其对应的颜色?x是通过rand() % 10获得的一个随机数

查找 x 对应的 pos:

思想:if(x小于2){x对应的是红色,即下标0}

else if(x大于等于2且小于7){x对应的是绿色,即下标1}

else{x大于等于7且小于10}{x对应的是黄色,即下标2}

上面的 if else 逻辑本质上是在找第一个pos,这个 pos 满足下列条件

for(i 从 0 到 n - 1)

        if((pos == 0 || x >= presum[pos - 1]) && x < presum[pos])

                return pos

然而对于上述的代码有进一步的优化,

 我们注意到正数数组对应的前缀和数组是严格递增数组 

所以可以使用二分查找算法以 O(logn) 的时间复杂度找到 pos

好了好了,上代码!查找 pos 的代码如下

int sum = _weight.back();
int randnum = rand() % sum + 1;
return lower_bound(_weight.begin(), _weight.end(), randnum) - _weight.begin();

 对于lower_bound() 这个接口的说明

具体说明请看链接std::lower_bound - cppreference.com

template< class ForwardIt, class T = typename std::iterator_traits<ForwardIt>::value_type >
constexpr ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value );

对于一个严格升序的数组nums,给定一个目标值 x,从左到右找到第一个pos,

这个pos满足 nums[pos] >= x,返回指向pos的迭代器。如果一直找不到,返回nums.end()

注意:

查找pos的代码中 randnum加上 1 就是为了保证找的nums[pos]不是 >= x,而是大于x

好了好了,整合一下代码

class Solution {
    vector<int> _weight;
public:
    Solution(vector<int> w) {
        _weight = move(w);
        partial_sum(_weight.begin(), _weight.end(), _weight.begin());
    }
    
    int pickIndex() {
        int sum = _weight.back();
        int randnum = rand() % sum + 1;
        return lower_bound(_weight.begin(), _weight.end(), randnum) - _weight.begin();
    }
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* obj = new Solution(w);
 * int param_1 = obj->pickIndex();
 */


http://www.kler.cn/a/468024.html

相关文章:

  • C语言:枚举类型
  • CODESYS MODBUS TCP通信(AM400PLC作为主站通信)
  • C++:位与运算符
  • 苍穹外卖 项目记录 day03
  • Kafka 消费者专题
  • 【可实战】测试用例组成、用例设计方法、用例编写步骤、测试用例粒度、用例评审(包含常见面试题)
  • 记录一下core-js安装报错(core-js/modules/es.array.push.js core-js/modules/es.error.cause.js)
  • 简历_专业技能_熟悉Redis常用数据结构及其操作命令
  • 现代光学基础4
  • 如何下载 Chrome 历史版本 - 完整指南
  • SwiftUI 撸码常见错误 2 例漫谈
  • EPS32基础篇开发
  • vue 如何实现复制和粘贴操作
  • 获取钉钉微应用免登授权码(h5微应用)
  • 创建.net core 8.0项目时,有个启用原生AOT发布是什么意思
  • Oracle 创建本地用户,授予权限,创建表并插入数据
  • SQL中,# 和 $ 用于不同的占位符语法
  • 在 Python 中合并多个 Word 文档
  • spring防止重复点击,两种注解实现(AOP)
  • [开源]C++代码分享
  • 基于Spring Boot微信小程序的房产交易租赁服务平台
  • 慧集通iPaaS集成平台低代码训练-实践篇
  • 术业有专攻,遨游工业三防手机筑牢“危急特”通信防线
  • Ubuntu离线登入mysql报错缺少libncurses.so.5问题
  • CSS 之 响应式设计 前世今生
  • Java 集合框架之 List、Set 和 Map 的比较与使用