从Python的random.sample种移除一个单独的元素

huangapple go评论64阅读模式
英文:

Remove a single element from python's random.sample population

问题

以下是您要翻译的内容:

我正在尝试实现一个图的表示,其中图的每个节点都与其他节点具有随机的边,但不与自己相连。因此,我试图在random.sample生成随机邻居的群体中排除节点本身。然而,以下代码返回"TypeError: Population must be a sequence. For dicts or sets, use sorted(d)."

import random as rnd
gsize=5
graph = [rnd.sample(list(range(gsize)).remove(node), rnd.randint(0, gsize-1)) for node in range(gsize)]

我在这个问题中看到建议首先创建一个变量,然后对其应用remove()。我试图以以下方式重构它:

glist = list(range(gsize))
graph = [rnd.sample(glist.remove(node), rnd.randint(0, gsize-1)) for node in range(gsize)]

但现在它抛出"TypeError: 'NoneType' object is not iterable"。我认为问题本质上是相同的,即群体的类型是NoneType。有没有一种简洁的方法可以实现我需要的功能?

英文:

I am trying to implement a graph representation where each node of the graph has random edges to other nodes but not to itself. Therefore, I am trying to exclude the node itself from the population out of which random.sample would generate the random neighbors. However, the following code returns "TypeError: Population must be a sequence. For dicts or sets, use sorted(d)."

import random as rnd
gsize=5
graph = [rnd.sample(list(range(gsize)).remove(node), rnd.randint(0, gsize-1)) for node in range(gsize)]

I saw in this question it was suggested to create a variable first and then apply remove() to it. I tried to refactor it this way

glist = list(range(gsize))
graph = [rnd.sample(glist.remove(node), rnd.randint(0, gsize-1)) for node in range(gsize)]

but now it throws "TypeError: 'NoneType' object is not iterable". I think the problem is essentially the same, i.e. the population is of type NoneType. Is there a neat way of achieving what I need?

答案1

得分: 2

list.remove是一个原地操作,因此返回None。你可能想要的是类似这样的操作:

graph = [
    rnd.sample(
       [other for other in range(gsize) if not node == other], 
       rnd.randint(0, gsize-1)
    ) for node in range(gsize)
]

在这里,你每次都在构建一个不包括当前节点的集合。

一个更有效的方法是:

from collections import deque
import random as rnd

graph = []

population = deque(range(gsize))

for _ in range(gsize):
    # 弹出最右边的元素
    removed = population.pop()

    # 用剩余的人口建立图
    graph.append(rnd.sample(population, rnd.randint(0, gsize-1)))

    # 将移除的元素添加到最左边
    population.appendleft(removed)
英文:

list.remove is an in-place operation and thus returns None. What you probably want is something like:

graph = [
    rnd.sample(
       [other for other in range(gsize) if not node == other], 
       rnd.randint(0, gsize-1)
    ) for node in range(gsize)
]

Where you are building a non-inclusive set every time.

A more efficient way would be:

from collections import deque
import random as rnd

graph = []

population = deque(range(gsize))

for _ in range(gsize):
    # pop the rightmost element
    removed = population.pop()

    # build the graph with the remaining population
    graph.append(rnd.sample(population, rnd.randint(0, gsize-1)))

    # add the removed element to the leftmost side
    population.appendleft(removed)

</details>



# 答案2
**得分**: 1

你可以使用列表推导来创建不包含当前节点的人口就像这样
```python
import random

gsize = 5
graph = [random.sample([n for n in range(gsize) if n != node], random.randint(0, gsize - 1)) for node in range(gsize)]
英文:

You can use list comprehension to create the population without the current node, like this:

import random

gsize = 5
graph = [random.sample([n for n in range(gsize) if n != node], random.randint(0, gsize - 1)) for node in range(gsize)]

答案3

得分: 1

您可以从比gsize小1的范围中取样当值与当前索引相同或大于当前索引时添加1这将有效地跳过当前索引而无需处理任何排除或项删除

import random as rnd
gsize = 5
graph = [[n + (n >= r) for n in rnd.sample(range(gsize - 1), rnd.randrange(gsize))]
         for r in range(gsize)]

output:

print(*graph, sep="\n")

[1, 2]
[4, 3]
[0, 4]
[0]
[3, 1, 0, 2]

如果您想获得一个无向图那么您需要使这些邻居成为双向的并且在节点之间保持一致为此您可以使用itertools中的组合来生成一个方向上的链接列表leftIndex < rightIndex),根据所需的密度随机选择这些链接的数量然后在图矩阵中同时分配引用

gsize = 5
allLinks = gsize * (gsize - 1) // 2
linkCount = allLinks * 75 // 100  # 75% density

graph = [[] for _ in range(gsize)]
from itertools import combinations
for r, c in rnd.sample([*combinations(range(gsize), 2)], linkCount):
    graph[r].append(c)
    graph[c].append(r)

output:

print(*graph, sep="\n")

[4, 3]
[2, 4, 3]
[1, 4]
[4, 1, 0]
[3, 0, 1, 2]

*请注意 `node0 --> node4` 和对称地 `node4 --> node0`*
英文:

You could take a sample from a range that is one less than gsize and add one when the value is the same or greater than the current index. This will effectively skip over the current index without having to bother with any exclusion or item removal:

import random as rnd
gsize = 5
graph = [ [n+(n&gt;=r) for n in rnd.sample(range(gsize-1),rnd.randrange(gsize))] 
          for r in range(gsize)]

output:

print(*graph,sep=&quot;\n&quot;)

[1, 2]
[4, 3]
[0, 4]
[0]
[3, 1, 0, 2]

If you want to obtain an undirected graph, then you will need these "neighbours" to be bidirectional and consistent across nodes. For this, you can use combinations from itertools to produce a list of links in one direction (leftIndex < rightIndex), select a number of these links at random according to the desired density, and then assign the references in both directions in the graph matrix:

gsize     = 5
allLinks  = gsize*(gsize-1)//2
linkCount = allLinks * 75 // 100     # 75% density

graph    = [[] for _ in range(gsize)]
from itertools import combinations
for r,c in rnd.sample([*combinations(range(gsize),2)],linkCount):
    graph[r].append(c)
    graph[c].append(r)

output:

print(*graph,sep=&quot;\n&quot;)

[4, 3]
[2, 4, 3]
[1, 4]
[4, 1, 0]
[3, 0, 1, 2]

Note how node0 --&gt; node4 and symmetrically node4 --&gt; node0

huangapple
  • 本文由 发表于 2023年7月17日 21:30:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704976.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定