【问题标题】:N random, contiguous and non-overlapping subsequences each of lengthN 个随机、连续且不重叠的子序列,每个子序列的长度
【发布时间】:2013-09-09 13:54:55
【问题描述】:

我正在尝试获取序列的 n 个随机且不重叠的切片,其中每个子序列的长度为 l,最好按照它们出现的顺序。

这是我目前拥有的代码,每次尝试使其工作变得越来越混乱,不用说它不起作用。

def rand_parts(seq, n, l):
    """
    return n random non-overlapping partitions each of length l.
    If n * l > len(seq) raise error.
    """
    if n * l > len(seq):
        raise Exception('length of seq too short for given n, l arguments')
    if not isinstance(seq, list):
        seq = list(seq)
    gaps = [0] * (n + 1)
    for g in xrange(len(seq) - (n * l)):
        gaps[random.randint(0, len(gaps) - 1)] += 1
    result = []
    for i, g in enumerate(gaps):
        x = g + (i * l)
        result.append(seq[x:x+l])
        if i < len(gaps) - 1:
            gaps[i] += x
    return result

例如,如果我们说rand_parts([1, 2, 3, 4, 5, 6], 2, 2),它可以从下图中返回 6 个可能的结果:

[1, 2, 3, 4, 5, 6]
 ____  ____

[1, 2, 3, 4, 5, 6]
 ____     ____ 

[1, 2, 3, 4, 5, 6]
 ____        ____ 

[1, 2, 3, 4, 5, 6]
    ____  ____ 

[1, 2, 3, 4, 5, 6]
    ____     ____ 

[1, 2, 3, 4, 5, 6]
       ____  ____

所以[[3, 4], [5, 6]] 可以接受,但[[3, 4], [4, 5]] 不会,因为它重叠,[[2, 4], [5, 6]] 也不会,因为[2, 4] 不连续。

我在做一些代码打高尔夫球时遇到了这个问题,所以出于兴趣考虑,同时看到一个简单的解决方案和/或一个高效的解决方案,对我现有的代码不太感兴趣。

【问题讨论】:

  • 我认为你应该尝试递归......
  • 当有重复值时会发生什么?

标签: python list random


【解决方案1】:
def rand_parts(seq, n, l):
    indices = xrange(len(seq) - (l - 1) * n)
    result = []
    offset = 0
    for i in sorted(random.sample(indices, n)):
        i += offset
        result.append(seq[i:i+l])
        offset += l - 1
    return result

要理解这一点,首先考虑案例l == 1。然后它基本上只是按排序顺序返回输入数据的random.sample();在这种情况下,offset 变量始终为 0。

l &gt; 1 的情况是前一种情况的扩展。我们使用random.sample() 来获取位置,但保持offset 来转移连续结果:通过这种方式,我们确保它们是不重叠的范围——即它们从至少l 的距离开始彼此,而不是 1。

【讨论】:

  • 非常好。唯一可以做得更快(但不那么优雅)的是排序,在这种情况下,它可以在 O(n) 时间和 O(n) 空间中工作。但是在 Python 中,没有真正的数组,这一点是毫无意义的。
  • 这很好用,random.sample 处理了我在代码中处理的错误情况。我想这可能看起来很简单,但我无法理解 len(seq) - (l - 1) * n 是什么以及你是如何确定它的。
  • @RussW:它的工作原理已经在答案中说明了......让我尝试改写它。 L=1 的情况当然是微不足道的,只要做一个随机样本就可以了。现在将所有其他元素视为“额外”,暂时忽略它们,只关注子序列的“头部”。那些“额外”元素是n*(L-1),所以你把它们拿走,随机选择“头”,然后通过添加正确的“尾”来计算结果。不要因为没有立即理解而感到羞耻,在与 Armin 交谈时经常会发生这种情况……他尽力了,但他太聪明了:-D
  • @6502 谢谢。所以(l - 1) * n 代表尾部的总和,random.sample 从 len(seq) 的范围内随机选择头部的索引 - 尾部的总和。我只是想把它形象化,这样我以后就可以很容易地达到它。毕竟其实很简单。
【解决方案2】:

许多解决方案都可以解决这个问题,但如果序列要严格随机,则必须小心。例如,从 0 到 len(seq)-n*l 之间选择一个随机数并说第一个序列将从那里开始,然后递归工作是错误的。

这个问题相当于随机选择n+1 整数,使得它们的总和等于len(seq)-l*n。 (这些数字将是您的序列之间的“间隙”。)要解决它,您可以查看this question

【讨论】:

  • 你指出的问题在这种情况下没有用:它是关于实数的。
  • 我不认为四舍五入会有这么大的问题,所以我认为它是相关的。
  • 3.4 + 2.4 + 0.2 == 6.0round(3.4) + round(2.4) + round(0.2) == 5.0,或多或少有任何舍入的定义。
  • @ArminRigo,你不会像这样简单地舍入,你必须跟踪小数部分,但这不是问题。
  • 我认为这就是我试图在我的代码中做的,但没有像你那样定义它。
【解决方案3】:

这在 Python 3.3.2 中对我有用。它应该向后兼容 Python 2.7。

from random import randint as r

def greater_than(n, lis, l):
    for element in lis:
        if n < element + l:
            return False
    return True

def rand_parts(seq, n, l):
    """
    return n random non-overlapping partitions each of length l.
    If n * l > len(seq) raise error.
    """
    if n * l > len(seq):
        raise(Exception('length of seq too short for given n, l arguments'))
    if not isinstance(seq, list):
        seq = list(seq)
    # Setup
    left_to_do = n
    tried = []
    result = []
    # The main loop
    while left_to_do > 0:
        while True:
            index = r(0, len(seq) - 1)
            if greater_than(index, tried, l) and index <= len(seq) - left_to_do * l:
                tried.append(index)
                break
        left_to_do -= 1
        result.append(seq[index:index+l])
    # Done
    return result

a = [1, 2, 3, 4, 5, 6]
print(rand_parts(a, 3, 2))

上面的代码总是会打印 [[1, 2], [3, 4], [5, 6]]

【讨论】:

  • 这并没有给出相等的概率。例如:a = [1, 2, 3]; count = {(1,2):0, (1,3):0, (2,3):0}; for i in range(2000): [x], [y] = rand_parts(a, 2, 1); count[x, y] += 1; print count。它选择解决方案 [[2], [3]] 大约有一半的时间,而其他两个解决方案各只有四分之一的时间。
【解决方案4】:

如果你递归地做它会简单得多。取第一部分(所以其余部分适合):

 [0:total_len - (numer_of_parts - 1) * (len_of_parts)]

然后递归处理剩下的事情:

rand_parts(seq - begining _to_end_of_part_you_grabbed, n - 1, l)

【讨论】:

  • 这正是不会产生真正随机答案的解决方案。选择第一个的概率不应该是一致的,它应该根据您为第一个选择一个数字后存在的答案数量而有所偏差。
【解决方案5】:

首先,我认为您需要澄清随机一词的含义。

当您对子序列本身设置特定限制时,如何生成真正的随机子序列列表?

据我所知,在这种情况下,任何人都可以实现的最佳“随机性”是生成 all 满足您标准的子序列列表,并从池中选择您需要的任意数量一种随机的方式。

现在,根据我几年前参加的算法课的经验,您的问题似乎是一个典型示例,可以使用 贪婪算法 解决这些问题(但可能?)首先假设您实际上在问什么:

  • random 的真正意思是 not 应该随机生成子序列列表(这有点矛盾,正如我之前所说的),但是任何 可以产生的解决方案与其他解决方案一样有效(例如,6 个解决方案中的任何一个从输入 [1,2,3,4,5,6] 开始都是有效的,而您不这样做'不在乎哪一个)
  • 重申上述内容,您只需要一个可以生成的可能解决方案,并且您需要一种可以输出这些有效答案一个的算法。

假设这里是一个贪心算法,它在线性时间内生成一个子序列的可能列表(不包括排序,即 O(n*log(n))):

def subseq(seq, count, length):
    s = sorted(list(set(seq)))

    result = []
    subseq = []

    for n in s:
        if len(subseq) == length:
            result.append(subseq)
            if len(result) == count:
                return result
            subseq = [n]
        elif len(subseq) == 0:
            subseq.append(n)
        elif subseq[-1] + 1 == n:
            subseq.append(n)
        elif subseq[-1] + 1 < n:
            subseq = [n]

    print("Impossible!") 

算法的要点如下:

  • 您的要求之一是不能有任何重叠,这最终意味着您需要处理唯一编号和仅唯一编号。所以我使用 set() 操作来消除所有重复项。然后我对其进行排序。
  • 休息很简单。我只是遍历排序列表并贪婪地形成子序列。
  • 如果算法不能形成足够数量的子序列,则打印“不可能!”

希望这就是你要找的。​​p>

编辑:由于某种原因,我错误地认为子序列中不能有重复值,这个允许它。

def subseq2(seq, count, length):
    s = sorted(seq)

    result = []
    subseq = []

    for n in s:
        if len(subseq) == length:
            result.append(subseq)
            if len(result) == count:
                return result
            subseq = [n]
        elif len(subseq) == 0:
            subseq.append(n)
        elif subseq[-1] + 1 == n or subseq[-1] == n:
            subseq.append(n)
        elif subseq[-1] + 1 < n:
            subseq = [n]

    print("Impossible!")

【讨论】:

  • 我明白你的意思。在试图定义问题时,我停下来思考了一下,但这与“随机性”并不矛盾。返回随机整数的问题,即random.randint(a, b),受结果为整数和边界 a 和 b 的约束。类似地,有 p 种不同的可能方法来检索 n 个长度为 l 的子序列,这些子序列是连续的且不重叠的。所以问题是从 p 个可能性的集合中返回任何随机项。
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2012-06-30
  • 1970-01-01
  • 2020-01-19
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多