Java 非均匀权重集的绘图实现
我想讨论如何以最佳方式实施以下内容: 给定一组元素 它们都是不同的。调用此输入集Java 非均匀权重集的绘图实现,java,algorithm,optimization,language-agnostic,Java,Algorithm,Optimization,Language Agnostic,我想讨论如何以最佳方式实施以下内容: 给定一组元素 它们都是不同的。调用此输入集N,它的大小为N。这些元素各有一个权重。现在,您需要创建大小为R的N的子集(R),其中R小于或等于N。 这个子集同样不能包含重复。R的每个元素都需要从N中随机选择,如果某个元素的权重更高,则其被选择的几率也需要更高。如果N中所有元素的总重量为W,则选择元素i的几率需要为W\u i/W 最后一个细节是权重很容易改变,但只会增加,而不会减少 我想要一个简洁的实现。我在Java中工作,但我希望找到一些有趣的与语言无关的属性
N
,它的大小为N
。这些元素各有一个权重。现在,您需要创建大小为R
的N
的子集(R
),其中R
小于或等于N
。
这个子集同样不能包含重复。R
的每个元素都需要从N
中随机选择,如果某个元素的权重更高,则其被选择的几率也需要更高。如果N
中所有元素的总重量为W
,则选择元素i
的几率需要为W\u i/W
最后一个细节是权重很容易改变,但只会增加,而不会减少
我想要一个简洁的实现。我在Java中工作,但我希望找到一些有趣的与语言无关的属性或细节(甚至是伪代码)
现在,对于我自己的解决方案:我创建一个原始元素集的数组列表。我确保权重是自然数,如果元素的权重是n
,我将它们加上n
。然后我洗牌数组列表(collections.shuffle
),并继续从洗牌列表中提取元素并将它们添加到JavaSet
,直到集合的大小为r
。如果一个元素的权重增加,它会被添加到原始数组列表中更多的次数。再次洗牌,创建一个新子集
我的实现需要大量内存,如果集变大,洗牌的速度也会变慢。有更好的主意吗?首先,让我们把它简化为只画一个元素,你可以计算
sum[-1] = 0
sum[i] = sum[i-1] + weight[i]
然后,您只需在范围[0,sum)
中绘制一个数字r
,然后对r
进行二进制搜索。它所处的范围就是您绘制的数字。
这是O(n)
时间解决方案
显然,您可以对更多元素执行此操作,但您必须从集合中删除已选择的元素,或重复此操作,直到选择新项。但是,对于较大的子集,这两种解决方案都会退化为二次复杂度:(
但是,我们能改进它,使之做得更好吗?
是的。使用二元搜索树而不是数组。二元搜索树实际上是的一种变体,其中不是存储#children(v)
,而是存储每个子树中的权重之和。除此之外-这基本上与顺序统计树保持相同
有关树解决方案的更多信息,请参见类似问题的答案:
构建树的复杂性是O(nlogn)
,每个查询+删除都是O(logn)
,这将为您提供O(nlogn+klogn)=O(nlogn)
因此,我们有两个选择:
如果o(logn)
中的k
(此处),则更喜欢在每个查询中使用o(n)
时间重新创建数组。否则,您应该更喜欢(在时间复杂度方面)o(nlogn)
基于树的解决方案。
就空间而言,两种解决方案在所需的额外空间量上都是线性的
这甚至可以通过一次传递做得更好。这被称为。它的主要缺点是由于指数部分(至少从我的经验来看)导致的较大权重的数值问题导致不稳定
此解决方案以线性时间运行,具有O(1)
额外空间(如果不包括大小为k
的输出数组)