如何在屏幕上有效地绘制N个点?

时间:2016-04-02 21:52:48

标签: python algorithm

这听起来像一个简单的问题,但我发现以良好的表现获得正确是非常棘手的。

我提出的第一个算法是随机绘制点,从集合中检查它是否已被绘制,否则绘制它。如果我们绘制几个点但是当我们接近填充屏幕时灾难性地减速,这种方法很好。

我想出的最好的方法是构造像素列表,对其进行随机播放并选择前n个(我使用了python' random.sample)。它工作得更好,但仍然有点慢,因为整个像素列表需要在内存中构建,这在绘制5个点时非常难以克服。这是我的python代码:

#!/usr/bin/env python
""" drawn n random points on the screen """
import pygame
from pygame.locals import *
import sys
import random
from itertools import product

n = int(sys.argv[1])
s = pygame.display.set_mode()
sx, sy = s.get_size()

points = random.sample(list(product(range(sx), range(sy))), n)

for p in points:
    s.fill((255, 255, 255), pygame.Rect(*p, 1, 1))
pygame.display.flip()
while True:
    for event in pygame.event.get():
        if event.type == QUIT or event.type == KEYDOWN:
            sys.exit()

有关更好算法的任何建议吗?

编辑:刚发现这个问题被称为"水库采样"。维基百科有许多好的算法:https://en.wikipedia.org/wiki/Reservoir_sampling

3 个答案:

答案 0 :(得分:4)

来自懒惰序列的样本:

defaults delete com.apple.dt.Xcode

points = [(i // sy, i % sy) for i in random.sample(xrange(sx*sy), n)] 将根据序列和样本的相对大小,选择是否实现序列并执行部分混洗或选择随机元素并跟踪选定的索引。

请注意,它必须是一个实际的序列,而不是迭代器,才能使其正常工作。与普遍看法相反,random.sample(或Python 3 xrange)是一个实际的序列。发电机不能在这里工作。

答案 1 :(得分:2)

如果您要绘制如此多的积分以填满屏幕,那么您可能不想列出它们或记住您绘制的所有点。

您要做的是在点之间创建伪随机,可逆映射。调用映射E(x,y)。然后,您可以按扫描线或其他顺序生成所有点(x,y),然后对于每个点(x,y),您可以在屏幕上绘制E(x,y)。通过确保映射是可逆的,您可以确保每个唯一的(x,y)映射到唯一的E(x,y),因此您绘制的每个点都是不同的。

制作像E(x,y)这样的函数的一种常用方法是使用Feistel结构:https://en.wikipedia.org/wiki/Feistel_cipher

这用于制作许多加密密码,如DES。

在你的情况下,你可以从良好的整数散列函数H(x)开始,然后给出W =屏幕宽度和H =屏幕高度,{{1 }} =要使用的轮数(5ish会这样做),你可以使你的函数像这样(伪代码,而不是python,抱歉):

N

请注意,每个步骤都很容易反转。如果要撤消function E(x,y) for (i = 1 to N) x = (x+(H(y)%W)) % W; y = (y+(H(x)%H)) % H return (x,y) ,您可以执行y = (y+(H(x)%H)) % H(这是伪代码,因此我可以假设模数运算符与负数一起正常工作)。

即使该函数显然是可逆的,因为每一步都是可逆的,Feistel结构提供了良好的混合,如果你使用一个好的散列H,你的点将以一个很好的伪随机顺序出现。

答案 2 :(得分:0)

嗯,你可能想要考虑它们之间距离最小的采样点,从而使它们不重叠。我想到了泊松盘采样。可以在此处找到描述和Python代码:http://devmag.org.za/2009/05/03/poisson-disk-sampling/