英文:
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>=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]
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="\n")
[4, 3]
[2, 4, 3]
[1, 4]
[4, 1, 0]
[3, 0, 1, 2]
Note how node0 --> node4
and symmetrically node4 --> node0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论