首页

▼ 算法合集

▼ 加密算法

▼ 凯撒密码

凯撒密码

▼ Hill密码算法

Hill密码算法

▼ 多项式哈希算法

多项式哈希算法

▼ Rail Fence Cipher

Rail Fence Cipher

▼ Graph算法

▼ 关节点

关节点

▼ Bellman-Ford算法

Bellman-Ford算法

▼ 广度优先搜索

广度优先搜索

▼ GraphBridges

桥接模式

▼ 深度优先搜索

深度优先搜索

▼ 检测循环

检测循环

▼ Dijkstra算法

Dijkstra算法

▼ Eulerian Path

欧拉路径

▼ Floyd-Warshall算法

Floyd-Warshall算法

▼ HamiltonianCycle

Hamiltonian Cycle

▼ Kruskal算法

Kruskal算法

▼ Prim算法

Prim算法

▼ 强连通分量

强连通分量

▼ 拓扑排序

拓扑排序

▼ Travelling Salesman Problem

Travelling Salesman

▼ 图像处理算法

▼ Seam Carving算法

内容感知缩放算法

▼ 链表

▼ 反向遍历

反向遍历

▼ GraphTraversal

GraphTraversal

▼ 数学算法

▼ 二进制浮点数

BinaryFloatingPoint

▼ 位操作

位操作算法

▼ 复数

复数

▼ 欧几里得算法

Euclidean Algorithm

▼ Euclidean Distance

欧几里得距离

▼ 阶乘算法

阶乘算法

▼ 快速幂算法

快速幂算法

▼ Fibonacci数列

斐波那契数列

▼ 傅里叶变换

Fourier变换

▼ Horner法

霍纳法则

▼ 整数划分

整数划分

▼ 判断是否为2的幂

判断是否为2的幂

▼ 最小公倍数

最小公倍数

▼ Liu Hui

Liu Hui

▼ 矩阵

Matrix

▼ Pascal三角形

Pascal三角形

▼ Primality Test

素数测试

▼ 质因数

质因数

▼ 弧度计算

弧度计算

▼ 埃拉托色尼筛法

埃拉托色尼筛法

▼ SquareRoot

SquareRoot

▼ MachineLearning

▼ K均值算法

K均值算法

▼ K近邻算法

K近邻算法

▼ 搜索算法

▼ 二分查找算法

二分查找

▼ 插值搜索算法

插值搜索算法

▼ 跳跃搜索算法

跳跃搜索算法

▼ 线性搜索

线性搜索算法

▼ 集合

▼ 笛卡尔积

笛卡尔积

▼ 组合总和

组合总和

▼ 组合算法

组合算法

▼ Fisher-Yates洗牌算法

Fisher-Yates洗牌算法

▼ 背包问题

背包问题

▼ 最长公共子序列

最长公共子序列

▼ 最长递增子序列

最长递增子序列

▼ 最大子数组

最大子数组

▼ 排列组合

排列组合

▼ 幂集

幂集算法

▼ 最短公共超序列

最短公共超序列

▼ Sorting Algorithms

▼ 冒泡排序

冒泡排序

▼ 桶排序算法

桶排序算法

▼ 计数排序算法

计数排序

▼ 堆排序算法

堆排序

▼ 插入排序

插入排序

▼ 归并排序

归并排序

▼ 快速排序算法

快速排序算法

▼ 基数排序

基数排序

▼ 选择排序算法

选择排序算法

▼ 希尔排序

希尔排序

▼ 统计学

▼ 加权随机

加权随机算法

▼ 字符串算法

▼ Hamming距离

Hamming距离

▼ KnuthMorrisPratt算法

Knuth-Morris-Pratt算法

▼ LevenshteinDistance

Levenshtein距离

▼ 最长公共子串

最长公共子串

▼ 回文检测算法

回文检测算法

▼ Rabin-Karp算法

Rabin-Karp算法

▼ 正则表达式匹配

正则表达式匹配

▼ Z算法

Z算法

▼ Tree Data Structure

▼ 广度优先搜索

广度优先搜索

▼ 深度优先搜索

深度优先搜索

▼ 未分类

▼ 最佳买卖股票时机

最佳买卖股票时机

▼ 汉诺塔算法

HanoiTower

▼ 跳跃游戏算法

跳跃游戏

▼ KnightTour

骑士巡逻

▼ N皇后问题

N皇后问题

▼ 雨水收集

雨水收集

▼ 递归楼梯问题

递归楼梯问题

▼ 方阵旋转

方阵旋转

▼ 独特路径

UniquePaths

▼ 数据结构

▼ BloomFilter算法

布隆过滤器

▼ 不相交集数据结构

Disjoint Set

▼ 双向链表

双向链表

▼ Graph

Graph算法

▼ 哈希表

哈希表

▼ Heap数据结构

Heap数据结构

▼ 链表

链表

▼ LRU缓存

LRU缓存

▼ 优先队列

优先队列

▼ 队列

队列

▼ 栈结构

栈结构

▼ Tree Data Structure

树结构

▼ AVL树

AVL树

▼ 二叉搜索树

二叉搜索树

▼ Fenwick树

Fenwick树

▼ 红黑树

红黑树

▼ 线段树

SegmentTree

▼ Trie数据结构

Trie数据结构

加权随机

加权随机

什么是“加权随机”

假设你有一个项目列表。项目可以是任何东西。例如,我们可能有一个你喜欢食用的水果和蔬菜列表:['🍌', '🍎', '🥕']

权重列表代表每个项目的权重(或概率,或重要性)。权重是数字。例如,权重列表[3, 7, 1]表示:

  • 你会更频繁地吃🍎 苹果(在总共3 + 7 + 1 = 11次中选择),
  • 然后你会较少频繁地吃香蕉 🍌(在总共11次中只有3次),
  • 而你真的不喜欢胡萝卜 🥕(在总共11次中只选择1次)。

如果我们用概率来表述,那么权重列表可能是一个浮点数数组,总和为1(即[0.1, 0.5, 0.2, 0.2])。

在这种情况下的加权随机将是这样一个函数,它会随机从列表中返回一个项目,并且它会考虑每个项目的权重,以便权重较高的项目被选中的频率更高。

函数接口示例:

const items =   [ '🍌', '🍎', '🥕' ];
const weights = [  3,    7,    1  ];

function weightedRandom(items, weights) {
  // 实现代码在这里 ...
}

const nextSnackToEat = weightedRandom(items, weights); // 可能是 '🍎'

加权随机应用

算法

直接方法将是:

  1. 根据每个项目的权重重复列表中的每个项目。
  2. 从列表中随机选择一个项目。

例如,在我们的水果和蔬菜案例中,我们可以生成以下大小为3 + 7 + 1 = 11的列表:

const items =   [ '🍌', '🍎', '🥕' ];
const weights = [  3,    7,    1  ];

// 根据权重重复项目。
const weightedItems = [
  '🍌', '🍌', '🍌',
  '🍎', '🍎', '🍎', '🍎', '🍎', '🍎', '🍎',
  '🥕',
];

// 现在只需从weightedItems数组中随机选择一个项目。

然而,如你所见,这种方法可能需要大量内存,如果我们在weightedItems列表中有许多需要重复的项目。可以将其视为你需要重复字符串"some-random-string"18字节)一千万次的情况。仅此数组就需要分配大约180Mb的额外内存空间。

更高效的方法将是:

  1. 准备每个项目的累积权重列表(即cumulativeWeights列表,其元素数量与原始weights列表相同)。在我们的案例中,它将如下所示:cumulativeWeights = [3, 10, 11]
  2. 0到最高累积权重值生成随机数randomNumber。在我们的案例中,随机数将在[0..11]范围内。假设我们有一个randomNumber = 8
  3. 从左到右遍历cumulativeWeights列表,并选择第一个大于或等于randomNumber的元素。我们将使用这个元素的索引来从items数组中选择项目。

这种方法背后的思想是,权重较高的项目将“占据”更多的数值空间。因此,随机数落在“较高权重数值桶”的机会更大。

const weights =           [3, 7,  1 ];
const cumulativeWeights = [3, 10, 11];

// 以伪表示法,我们可以这样思考cumulativeWeights数组。
const pseudoCumulativeWeights = [
  1, 2, 3,               // <-- [3] 个数
  4, 5, 6, 7, 8, 9, 10,  // <-- [7] 个数
  11,                    // <-- [1] 个数
];

这里是weightedRandom函数可能实现的一个例子:

/**
 * 根据其权重选择随机项目。
 * 权重较高的项目被选中的频率更高(概率更高)。
 *
 * 例如:
 * - items = ['banana', 'orange', 'apple']
 * - weights = [0, 0.2, 0.8]
 * - weightedRandom(items, weights) 在 80% 的情况下会返回 'apple',在 20% 的情况下会返回
 * 'orange',它永远不会返回 'banana'(因为选择香蕉的概率是 0%)
 *
 * @param {any[]} items
 * @param {number[]} weights
 * @returns {{item: any, index: number}}
 */
export default function weightedRandom(items, weights) {
  if (items.length !== weights.length) {
    throw new Error('Items and weights must be of the same size');
  }

  if (!items.length) {
    throw new Error('Items must not be empty');
  }

  // 准备累积权重列表。
  // 例如:
  // - weights = [1, 4, 3]
  // - cumulativeWeights = [1, 5, 8]
  const cumulativeWeights = [];
  for (let i = 0; i < weights.length; i += 1) {
    cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
  }

  // 在 [0...sum(weights)] 的范围内生成随机数
  // 例如:
  // - weights = [1, 4, 3]
  // - maxCumulativeWeight = 8
  // - 随机数的范围是 [0...8]
  const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
```javascript
const randomNumber = maxCumulativeWeight * Math.random();

// Picking the random item based on its weight.
// The items with higher weight will be picked more often.
for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
  if (cumulativeWeights[itemIndex] >= randomNumber) {
    return {
      item: items[itemIndex],
      index: itemIndex,
    };
  }
}

实现