帮我找一个好的算法?
我有一个装满n球的袋子. (举个例子,我们假设有28个球.)
这个包中的球每个都有1种颜色.袋中有<= 4种不同颜色的球. (让我们说红色,绿色,蓝色和紫色是可能的.) 我有三个水桶,每个水桶都有多少个它们最终需要的球.这些数字总数为n. (例如,假设铲斗A最终需要7个球,铲斗B最终需要11个球,铲斗C最终需要10个球.) 水桶也可能有或没有颜色限制 – 他们不接受的颜色. (铲斗A不接受紫色球或绿球.铲斗B不接受红球.铲斗C不接受紫色球或蓝色球.) 我需要有效和随机地分配球(所有可能性的概率相等). 我不能随意将球放入有空间接受它们的水桶中,因为这可能会让我遇到这样一种情况,即只剩下空间的桶不接受袋子里剩下的唯一颜色. 给出的是,总是至少有一种分配球的可能性. (我不会只有一个红色球袋,而一些数量> 0的水桶不接受红球.) 所有球都被认为是不同的,即使它们是相同的颜色. (铲斗C获得红球1而不是红球2的可能性之一不同于除了铲斗C获得红球2而不是红球1之外的所有内容相同的可能性.) 编辑以添加我的想法: 我不知道这是否具有所有可能性的相同概率,正如我所希望的那样.我还没有想出效率 – 它似乎并不太糟糕.
这包含一个断言,我不确定它是否总是如此.
如果你知道的话,请评论这些事情.
Choose a ball from the bag at random. (Call it "this ball".)
If this ball fits and is allowed in a number of buckets > 0:
Choose one of those buckets at random and put this ball in that bucket.
else (this ball is not allowed in any bucket that it fits in):
Make a list of colors that can go in buckets that are not full.
Make a list of balls of those colors that are in full buckets that this ball is allowed in.
If that 2nd list is length 0 (There are no balls of colors from the 1st list in the bucket that allows the color of this ball):
ASSERT: (Please show me an example situation where this might not be the case.)
There is a 3rd bucket that is not involved in the previously used buckets in this algorithm.
(One bucket is full and is the only one that allows this ball.
A second bucket is the only one not full and doesn't allow this ball or any ball in the first bucket.
The 3rd bucket is full must allow some color that is in the first bucket and must have some ball that is allowed in the second bucket.)
Choose, at random, a ball from the 3rd bucket balls of colors that fit in the 2nd bucket, and move that ball to the 2nd bucket.
Choose, at random, a ball from the 1st bucket balls of colors that fit in the 3rd bucket, and move that ball to the 3rd bucket.
Put "this ball" (finally) in the 1st bucket.
else:
Choose a ball randomly from that list, and move it to a random bucket that is not full.
Put "this ball" in a bucket that allows it.
Next ball.
最佳答案 这是一个O(n ^ 3)时间算法. (3来自桶的数量.)
我们首先绘制一个强力枚举算法,然后提取一个有效的计数算法,然后展示如何采样.
我们使用具有两个嵌套循环的算法进行枚举.外循环遍历球.每个球的颜色无关紧要;只是它可以放在某些水桶而不是其他水桶.在每个外部迭代的开始,我们有一个部分解决方案的列表(到目前为止考虑到球的分配).内环超过部分解;我们通过以所有有效方式扩展赋值,为新列表添加了几个部分解决方案. (初始列表有一个元素,空的赋值.)
为了更有效地计算解决方案,我们应用一种称为动态编程或行程编码的技术,具体取决于您如何看待它.如果两个部分解在每个桶中具有相同的计数(在算法的生命周期内具有O(n ^ 3)种可能性),则一个的所有有效扩展都是另一个的有效扩展,反之亦然.我们可以使用计数来注释列表元素,并丢弃除了部分解的每个“等价类”之外的所有代表.
最后,为了获得随机样本,而不是任意选择代表,当我们组合两个列表条目时,我们从每一侧按比例对该方的计数进行抽样.
使用Python代码(O(n ^ 4)以简化;可以进行数据结构改进).
#!/usr/bin/env python3
import collections
import random
def make_key(buckets, bucket_sizes):
return tuple(bucket_sizes[bucket] for bucket in buckets)
def sample(balls, final_bucket_sizes):
buckets = list(final_bucket_sizes)
partials = {(0,) * len(buckets): (1, [])}
for ball in balls:
next_partials = {}
for count, partial in partials.values():
for bucket in ball:
next_partial = partial + [bucket]
key = make_key(buckets, collections.Counter(next_partial))
if key in next_partials:
existing_count, existing_partial = next_partials[key]
total_count = existing_count + count
next_partials[key] = (total_count, existing_partial if random.randrange(total_count) < existing_count else next_partial)
else:
next_partials[key] = (count, next_partial)
partials = next_partials
return partials[make_key(buckets, final_bucket_sizes)][1]
def test():
red = {'A', 'C'}
green = {'B', 'C'}
blue = {'A', 'B'}
purple = {'B'}
balls = [red] * 8 + [green] * 8 + [blue] * 8 + [purple] * 4
final_bucket_sizes = {'A': 7, 'B': 11, 'C': 10}
return sample(balls, final_bucket_sizes)
if __name__ == '__main__':
print(test())