带权重的随机选择算法 :: labuladong的算法小抄 #996
Replies: 48 comments 12 replies
-
今天面Cisco就出了这道.... 细节啊,卡了我半天,总结就是一定要私下自己手写 |
Beta Was this translation helpful? Give feedback.
-
java方法,前缀数组下标从0开始,最终的结果不需要再-1了: class Solution {
private Random random = new Random();
private int[] preSum;
private int n;
public Solution(int[] w) {
this.n = w.length;
// 前缀数组,下标0就是原数组w[0]
preSum = new int[n];
// 下标0就是原数组w[0]
preSum[0] = w[0];
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i-1] + w[i];
}
}
public int pickIndex() {
// 保证取到[1, preSum[n-1]]的闭区间
int target = random.nextInt(preSum[n-1]) + 1;
// right可以从n-1开始,也可以从n开始,对结果的正确性没有影响
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
// 如果找到了,直接去当前的下标
if (preSum[mid] == target) {
left =mid;
break;
} else if (preSum[mid] > target) {
// 向左找,因为当前mid的值大于target,可能是"第一个大于target"的值,所以不能丢弃mid
// 如果mid的值不再需要了(最终不会取到现在的mid),那么就可以right=mid-1;
right = mid;
} else {
// 向右找,因为当前的mid的值比target小,永远不会取到了。所以left=mid+1;
left = mid + 1;
}
}
// left代表的含义:
// 当目标元素 target 不存在数组 nums 中时,搜索左侧边界的二分搜索的返回值可以做以下几种解读:
// 1、返回的这个值是 nums 中大于等于 target 的最小元素索引。
// 2、返回的这个值是 target 应该插入在 nums 中的索引位置。
// 3、返回的这个值是 nums 中小于 target 的元素个数。
return left;
}
} |
Beta Was this translation helpful? Give feedback.
-
不懂就问,为啥后面的这些,二分的写法都不用总结的,还是用这种大多数人用的呢 |
Beta Was this translation helpful? Give feedback.
-
等价于 random from [0...sum(w)-1],然后找这个值的右插入位置:
|
Beta Was this translation helpful? Give feedback.
-
为什么前缀和数组中 0 本质上是个占位符啊?这句话还是不太理解,就是为什么target的范围不是【0,7】? |
Beta Was this translation helpful? Give feedback.
-
@thy485280869 不看就滚,别搁这叽叽歪歪 |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
是不是不一定要用搜索左侧边界的二分法呀,我用了最普通的二分搜索也通过了,把返回值改成left就可以了(虽然和模板不一样,但是感觉这样好像更容易理解 |
Beta Was this translation helpful? Give feedback.
-
@WanrenDing 如果你能说清楚为什么这样写能过,那就算是真的理解了 |
Beta Was this translation helpful? Give feedback.
-
@WanrenDing 因为这道题每个位置必定占有一定权重w[i]不可能为0,所以preSum里不会有重复元素,那么二分找到的第一个位置必然就是左侧边界了;返回值left能保证是target应插入preSum的位置的原因是我们取的是左闭右开的区间,left会移动至mid的右边一位,所以能保证left最终落在恰好比target大的位置(当然你return right也行) |
Beta Was this translation helpful? Give feedback.
-
@labuladong 请问使用左右都闭合的区间怎么写,我用了你的左右都闭合的区间的二分方法没有成功。求大佬教教 |
Beta Was this translation helpful? Give feedback.
-
这题还可以用lottery算法,空间复杂度O(1)就可以做出来 |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
请问有朋友用 Python 写的吗 num = random.uniform(0, self.pre_sum[-1]) + 1 但是用 randint 在 leetcode 上跑就会报错 num = random.randint(0, self.pre_sum[-1]) + 1 |
Beta Was this translation helpful? Give feedback.
-
同样左右闭合的左侧边界二分搜索过不了,return是return left==0 ? 0 : left-1; 我太菜了www |
Beta Was this translation helpful? Give feedback.
-
打卡 二分搜索的妙用! |
Beta Was this translation helpful? Give feedback.
-
带权重:搜索左侧边界target = random[1, preSum[n-1]] w = [1,3,2,1] preSum = [0, 1, 4, 6, 7] target 的取值范围 |
Beta Was this translation helpful? Give feedback.
-
我倒是挺喜欢lol还有lol手游的匹配机制的,因为在这两款游戏中,我都是那条菜狗😋 |
Beta Was this translation helpful? Give feedback.
-
class Solution {
int [] presum = null;
Random random = new Random();
public Solution(int[] w) {
int n = w.length;
if(n==0)return;
presum = new int[n];
presum[0] = w[0];
for(int i = 1 ; i<n;i++){
presum[i] = presum[i-1]+w[i];
}
}
public int pickIndex() {
int n = presum.length;
int target = random.nextInt(presum[n-1])+1;
return left_bound(target);
}
public int left_bound(int target){
int left = 0 , right = presum.length-1;
while(left<=right){
int mid = left + (right-left)/2;
if(presum[mid]>target){
right = mid - 1;
}else if(presum[mid]<target){
left = mid + 1;
}else if(presum[mid] == target){
left = mid + 1;
}
}
if(right<0)return left;
return presum[right] == target? right: left;
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(w);
* int param_1 = obj.pickIndex();
*/ 这里我发现了一个小细节 首先前缀和可以不使用n+1 只用n 东哥可能是为了和前面讲的前缀和呼应 所以我这里写的代码的意思是 如果target能被找到 那么就返回right 找不到 那就乖乖返回left |
Beta Was this translation helpful? Give feedback.
-
class Solution {
// 前缀和数组
private preSum: number[] | undefined;
constructor(w: number[]) {
const n = w.length;
this.preSum = new Array(n + 1);
this.preSum[0] = 0;
for (let i = 1; i <= n; i++) {
this.preSum[i] = this.preSum[i - 1] + w[i - 1];
}
}
pickIndex(): number {
const last = this.preSum.slice(-1)?.[0];
const random = Math.ceil(Math.random() * last);
const i = this.preSum.findIndex(f => f >= random);
return i - 1;
}
} |
Beta Was this translation helpful? Give feedback.
-
东哥写的还是那么巧妙,感觉细节还是多的,比如随机从[1,7] |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
type Solution struct {
presum []int
}
func Constructor(w []int) Solution {
n:=len(w)
presum:=make([]int,n+1)
presum[0]=0
for i:=1;i<=n;i++{
presum[i] = presum[i-1] + w[i-1]
}
return Solution{presum}
}
func (this *Solution) PickIndex() int {
n:=len(this.presum)
target:=rand.Intn(this.presum[n-1])+1
return leftBound(this.presum,target) - 1
}
func leftBound(nums []int,target int) int{
if len(nums) == 0 {
return -1
}
left,right:=0,len(nums)
for left<right {
mid:=left + (right-left)>>2
if nums[mid] == target {
right = mid
}else if nums[mid] < target{
left = mid + 1
}else if nums[mid] > target{
right = mid
}
}
return left
}
/**
* Your Solution object will be instantiated and called as such:
* obj := Constructor(w);
* param_1 := obj.PickIndex();
*/ |
Beta Was this translation helpful? Give feedback.
-
提供用 二分搜尋找 gt ge lt le 的相關資料 其實有一定的規律 def find_lt(a, x): 解讀為 https://docs.python.org/3/library/bisect.html#searching-sorted-lists |
Beta Was this translation helpful? Give feedback.
-
生成随机数,int target = rand.nextInt(preSum[n - 1]) + 1; |
Beta Was this translation helpful? Give feedback.
-
前缀数组不用占位符也能做,即preSum[0] = w[0],而且这样感觉更好理解,不需要返回结果减1。不过这样二分搜索时要需要使用双闭区间,左闭右开会有问题,同时随机数的范围也要双闭 |
Beta Was this translation helpful? Give feedback.
-
20230110 打卡 |
Beta Was this translation helpful? Give feedback.
-
为啥用闭区间二分搜索做这个就通不过呢😰 |
Beta Was this translation helpful? Give feedback.
-
打卡 |
Beta Was this translation helpful? Give feedback.
-
没懂为什么不能用右边界 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:带权重的随机选择算法
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions