【问题标题】:Efficiently choosing x *different* elements in a list at random有效地随机选择列表中的 x *不同*元素
【发布时间】:2015-03-03 21:44:33
【问题描述】:

我有一个清单:

a = [1,2,1,1,3,5,6,2]

我想从这个列表中随机选择 3 个元素,但 它们必须都不同

我需要保留每个元素的“权重”,因此无法从 set(a) 采样。

到目前为止,我的解决方案是:

while condition == False:
    mysample = random.sample(a, 3)
    if len(set(mysample)) - len(mysample) !=0:
        condition = False
    else:
        condition = True

但这迫使我重新采样尽可能多的元素,以使元素都不同。这适用于小样本,但对于大样本,我的代码变得非常低效......

【问题讨论】:

  • 如果你给它一个包含重复项的列表,它可以返回重复项。 OP 想要一个没有重复但仍然加权的列表,这样更常见的元素比不太常见的元素更有可能出现。
  • @aruisdante random.sample(a, 3) 在我的机器上产生了[1, 1, 6]。它返回列表中三个1s 中的两个。
  • sample 1,然后删除原始列表中所有出现的那个。重复直到您拥有所需数量的元素。看看这里:stackoverflow.com/questions/1157106/…
  • @JohnKugelman 啊等等,他们后来确实说这是基于索引的唯一性,而不是价值。他们没有很好地强调这一点。我的错。

标签: python list select random weighted


【解决方案1】:

你可以随机抽取前三个不重复的元素:

import random
random.shuffle(your_list)
three_elements = set()
for v in your_list:
  if len(three_elements) == 3: break
  three_elements.add(v)

【讨论】:

  • 最高效(优雅!)的小列表。
【解决方案2】:
l = []
seen = set()
while len(l) < 3:
    ch = choice(a)
    if ch not in seen:
        l.append(ch)
        seen.add(ch)
print(l)

根据实际不同数字与元素的比例,不同的方法将具有不同的优势:

In [7]: a = [choice(range(10000)) for _ in range(100000)]

In [6]: import random

In [7]: a = [choice(range(10000)) for _ in range(100000)]

In [8]: %%timeit
random.shuffle(a)
three_elements = set()
for v in a:
    if len(three_elements) == 5000:
        break
    if not v in three_elements:
        three_elements.add(v)
   ...: 
10 loops, best of 3: 36.5 ms per loop

In [9]: %%timeit                          
l = []
seen = set()
while len(l) < 5000:
    ch = choice(a)
    if ch not in seen:
        l.append(ch)
        seen.add(ch)
   ...: 
100 loops, best of 3: 5.16 ms per loop

在 10 分钟后运行您的代码,我不得不退出该过程,因为它仍在运行,所以无论您选择什么都将是一个重大改进。

如果您的重复次数与列表中的实际项目的比例更高,并且您想要一个非常大的样本量,则使用 shuffle 会更有效,否则 shuffle 的成本将使其效率低于简单地使用 set 和 choice,

【讨论】:

    【解决方案3】:
    while count < sampleSize: # where sampeSize is the number of values you want
        s = random.sample(a, 1)
        filter(lambda x: x != s, a)
        mysample.append(s)
        count += 1
    

    【讨论】:

      【解决方案4】:

      这可能比必要的更复杂,但这里是使用reservoir sampling 的修改版本的实现。

      import itertools
      import random
      
      def element_at(iterable, index, default=None):
          return next(itertools.islice(iterable, index, None), default)
      
      def sample_unique(iterable, size):
          S = set()
          for index, item in enumerate(iterable):
              if len(S) < size:
                  S.add(item)
              else:
                  r = random.randint(0, index)
                  if r < size:
                      other = element_at(S, r)
                      if item not in S:
                          S.remove(other)
                          S.add(item)
          return S
      

      【讨论】:

        猜你喜欢
        • 2014-02-07
        • 2010-09-08
        • 2021-12-30
        • 1970-01-01
        • 2012-08-30
        • 1970-01-01
        • 1970-01-01
        • 2012-03-12
        • 2013-10-19
        相关资源
        最近更新 更多