Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2008/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 恒定时间随机选择和删除_Python_Data Structures_Graph - Fatal编程技术网

Python 恒定时间随机选择和删除

Python 恒定时间随机选择和删除,python,data-structures,graph,Python,Data Structures,Graph,我试图在Python中为多重图实现一个边列表 到目前为止我所尝试的: >>> l1 = Counter({(1, 2): 2, (1, 3): 1}) >>> l2 = [(1, 2), (1, 2), (1, 3)] l1对两个顶点之间的所有边进行固定时间删除(例如dell1[(1,2)]),但对这些边进行线性时间随机选择(例如random.choice(列表(l1.elements())))。请注意,您必须选择元素(而不是l1本身) l2具有恒定时间随机

我试图在Python中为多重图实现一个边列表

到目前为止我所尝试的:

>>> l1 = Counter({(1, 2): 2, (1, 3): 1})
>>> l2 = [(1, 2), (1, 2), (1, 3)]
l1
对两个顶点之间的所有边进行固定时间删除(例如
dell1[(1,2)]
),但对这些边进行线性时间随机选择(例如
random.choice(列表(l1.elements()))
)。请注意,您必须选择
元素
(而不是
l1
本身)

l2
具有恒定时间随机选择(
random.choice(l2)
),但所有元素的线性时间删除等于给定边(
[i对于l2中的i,如果i!=(1,2)]


问:是否有一种Python数据结构可以同时提供常量时间随机选择和删除功能?

我认为您试图做的事情在理论上是无法实现的

如果使用加权值表示重复项,则无法获得恒定时间随机选择。最好是使用某种跳过列表类型的结构,通过加权索引(即对数)对元素进行二进制搜索

如果您不使用加权值来表示副本,那么您需要一些允许存储多个副本的结构。哈希表不能做到这一点,DUP必须是独立的对象(例如,
(edge,autoincrement)
),这意味着无法在固定时间内删除所有符合某个标准的数据

如果你能接受对数时间,那么显而易见的选择是一棵树。例如,使用:

要随机选择一个:

>>> edge = random.choice(l3)
似乎并不能保证这不会起到什么作用。但幸运的是,两者的来源都表明它将做正确的事情。如果您不相信这一点,只需编写
l3[random.randrange(len(l3))]

要删除边缘的所有副本,可以按以下方式执行:

>>> del l3[l3.bisect_left(edge):l3.bisect_right(edge)]
或:

该文档解释了每项操作的确切性能保证。特别是,
len
是常量,而索引、切片、按索引或切片删除、平分和按值删除都是对数的,因此这两个操作都以对数结束

(值得注意的是,
blist
是一个B+树;您可能会从红黑树、treap或其他东西中获得更好的性能。您可以在PyPI上找到大多数数据结构的良好实现。)


正如senderle所指出的,如果一条边的最大副本数远小于集合的大小,则可以创建一个数据结构,该数据结构在时间上以最大副本数为准。将他的建议转化为代码:

class MGraph(object):
    def __init__(self):
        self.edgelist = []
        self.edgedict = defaultdict(list)
    def add(self, edge):
        self.edgedict[edge].append(len(self.edgelist))
        self.edgelist.append(edge)
    def remove(self, edge):
        for index in self.edgedict.get(edge, []):
            maxedge = len(self.edgelist) - 1
            lastedge = self.edgelist[maxedge]
            self.edgelist[index], self.edgelist[maxedge] = self.edgelist[maxedge], self.edgelist[index]
            self.edgedict[lastedge] = [i if i != maxedge else index for i in self.edgedict[lastedge]]
            del self.edgelist[-1]
        del self.edgedict[edge]
    def choice(self):
        return random.choice(self.edgelist)
(当然,您可以使用三行查找和更新来更改replace list with list comprehension行,但DUP的数量仍然是线性的。)

显然,如果你打算真正使用它,你可能会想加强一点课程。通过实现一些方法并让适当的/填充其余部分,可以使它看起来像一个边的
列表
,一个每个边的多个副本的
元组的
集合
,一个
计数器
,等等


那么,哪一个更好?在您的示例中,平均dup计数是列表大小的一半,最大值是列表大小的2/3。如果你的真实数据是这样的话,那么这棵树会好得多,因为
logn
显然会被
(N/2)**2
吹走。另一方面,如果DUP很少,senderle的解决方案显然会更好,因为如果
W
为1,
W**2
仍然为1

当然,对于一个3元素的样本,恒定的开销和乘数将主导一切。但想必你真正的收藏品并不是那么小。(如果是,只需使用
列表
…)


如果您不知道如何描述真实数据,请编写这两个实现,并使用各种实际输入对它们计时。

l1
列表吗?什么是
l1.elements()
dell1[(1,2)]
给了我一个
列表索引必须是整数,而不是元组
错误
l1
是一个列表;也许您在描述中颠倒了
l1
l2
?据我所知,没有内置的数据结构可以为您这样做。但是,您可以使用抽象基类创建类似dict的类,并具有选择随机项的附加功能。有关更多详细信息,请参阅。@abarnert,您肯定可以获得恒定时间随机选择,这正是
l2
实现它的方式。只需在列表中多次插入加权边。然后将索引列表字典维护到存在键的列表中。要删除项目,请从列表末尾弹出一个项目,并将其放在要删除的项目所在的位置;然后更新字典。确实,删除在最大权重中是线性的,因为您必须扫描索引列表才能更改索引。但是它接近OP所要求的。好吧,从较高的层次上说,我的edgedict实际上是跟踪edgelist中特定边缘占据的索引列表。当我需要删除一条边时,这些索引就会被使用,这样我就不必扫描整个列表。或者和森德勒说的一模一样。如果我不想删除一条边,而是想更改其中的一个节点(例如,我正在合并节点,需要相应地重定向边),则索引的dict仍然允许我在线性时间内这样做。我说的对吗?@MikeRand:再说一遍,重量是二次的,而不是线性的。但除此之外,是的。可以这样想:您总是可以通过删除和添加来模拟更改。若delete在权重上是二次的,add是常数,那个么在最坏的情况下,变更在权重上必须是二次的,对吗?
>>> try:
...     while True:
...         l3.remove(edge)
... except ValueError:
...     pass
class MGraph(object):
    def __init__(self):
        self.edgelist = []
        self.edgedict = defaultdict(list)
    def add(self, edge):
        self.edgedict[edge].append(len(self.edgelist))
        self.edgelist.append(edge)
    def remove(self, edge):
        for index in self.edgedict.get(edge, []):
            maxedge = len(self.edgelist) - 1
            lastedge = self.edgelist[maxedge]
            self.edgelist[index], self.edgelist[maxedge] = self.edgelist[maxedge], self.edgelist[index]
            self.edgedict[lastedge] = [i if i != maxedge else index for i in self.edgedict[lastedge]]
            del self.edgelist[-1]
        del self.edgedict[edge]
    def choice(self):
        return random.choice(self.edgelist)