生成半随机序列的有效方法

时间:2018-10-07 12:13:54

标签: algorithm

通常,我必须以某种半随机的方式生成数字序列,这意味着它不是完​​全随机的,而是必须具有其他一些属性。例如,我们需要一个1,2,3和4s的随机序列,但是没有数字必须连续重复三次。这些操作通常不是很复杂,但是我遇到了一个棘手的问题:我需要生成一个半随机序列,该序列有点长400多,由1,2,3和4s组成,每个数字必须出现相同的时间量(或者如果总和不能被四分之一整,而不是尽可能地将其整除),并且它们不能连续重复3次(所以1,3,4,4,4,4,2没问题)

我尝试方法:

  1. 创建一个具有所需长度和数量的列表;洗牌检查连续的数字是否正确,如果没有,请再次洗牌。

  2. 创建一个具有所需长度和数量的列表;生成所有排列并选择可以的排列;保存这些以供以后使用,并在需要时随机选择其中之一。

第一种方法运行了几分钟,然后生成了可以确定的任何序列,第二种方法产生了许多我的jupter笔记本放弃的排列。

这是第一个的python代码

from random import shuffle

v = []
for x in range(108):
    v += [1,2,3,4]
shouldicontinue = 1
while shouldicontinue:
    shuffle(v)
    shouldicontinue = 0
    for h in range(len(v)-1):
        if v[h] == v[h+1] and v[h] == v[h+2]:

            shouldicontinue = 1
            break
        else:
            pass

和第二个

from random import shuffle
import itertools
v = []
for x in range(108):
    v += [1,2,3,4]
good = []
for l in itertools.permutations(v):
    notok = 0
    for h in range(len(v)-1):
        if v[h] == v[h+1] and v[h] == v[h+2]:

            notok = 1
            break
        else:
            pass
    if not notok:
        good.append(v)

我正在寻找一种有效解决此问题的方法,即:如果它实时运行,则不需要花一分钟的时间就可以在速度较慢的计算机上生成,也可以事先准备好以某种方式(如方法2的想法),它可以在几个小时的中等水平的计算机上准备好。

2 个答案:

答案 0 :(得分:2)

在检查>400长度列表的所有排列之前,Universe可能已经死亡。因此,您需要另一种方法。

在这里,我建议尝试将元素随机插入列表中,但是当插入会违反其中一项要求时,移至下一个索引。

根据情况循环遍历元素,从 1 4 ,应确保始终可以插入。

from itertools import cycle, islice
from random import randint

def has_repeated(target, n, lst):
    """A helper to check if insertion would break the max repetition requirement"""
    count = 0
    for el in lst:
        count += el == target
        if count == n:
            return True
    return False

def sequence(length, max_repeat, elements=(1, 2, 3, 4)):
    # Iterator that will yield our elements in cycle
    values = islice(cycle(elements), length)

    seq = []
    for value in values:
        # Pick an insertion index at random
        init_index = randint(0, len(seq))

        # Loop over indices from that index until a legal position is found
        for shift in range(len(seq) + 1):
            index = init_index - shift
            slice_around_index = seq[max(0, index - max_repeat):index + max_repeat]

            # If the insertion would cause no forbidden subsequence, insert
            if not has_repeated(value, max_repeat, slice_around_index):
                seq.insert(index, value)
                break

        # This will likely never happen, except if a solution truly does not exist
        else:
            raise ValueError('failed to generate the sequence')
    return seq

样本

以下是一些示例输出,用于检查结果是否正确。

for _ in range(10):
    print(sequence(25, 2))

输出

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

在效率方面,生成具有相同要求的长度10,000的列表大约需要10毫秒。提示这对于大多数用途而言可能是足够有效的解决方案。

答案 1 :(得分:1)

我认为有可能(大约4 GB的内存和1分钟的预计算)生成比每个随机序列1秒更快的均匀分布的随机序列。

这个想法是为以下问题准备一个结果缓存:“有多少个正好为1s,b 2s,c 3s,d 4s的序列以特定数字的计数副本结尾?”

一旦有了此缓存,就可以计算满足约束条件的序列数(N),并可以通过选择介于1和N之间的随机数n并使用缓存生成n来随机生成一个序列第^个序列。

要在缓存中保存内存,您可以使用一些技巧:

  1. 答案在a / b / c / d中是对称的,因此您只需要使用a> = b> = c> = d
  2. 存储结果
  3. 按有效顺序,最后一位的计数始终为1或2

这些技巧应该意味着缓存仅需要保存大约4000万个结果。

相关问题