使用Python进行准随机化

时间:2011-01-07 22:07:39

标签: python random

问题在于:我尝试在两个元素之间选择n次(假设为[0,1] - > 0或1),我的最终列表将有n / 2 [0] + n / 2 [1]。我倾向于有这样的结果:[0 1 0 0 0 1 0 1 1 1 1 1 1 0 0,直到n]:问题是我不想连续4或5次相同的数字所以经常。我知道我可以使用准随机化程序,但我不知道该怎么做(我正在使用Python)。

5 个答案:

答案 0 :(得分:5)

为了保证存在相同数量的零和1,您可以生成包含n / 2个零和n / 2个的列表,并使用random.shuffle对其进行随机播放。

对于小n,如果您对结果超过您的验收标准(例如,没有太多连续相等的数字)感到不满意,请再次进行洗牌。请注意,执行此操作会降低结果的随机性,而不是增加它。

对于较大的n,使用此方法查找通过条件的结果将花费很长时间(因为大多数结果将失败)。相反,您可以使用以下规则一次生成一个元素:

  • 如果您已连续生成4个,则下一个数字必须为零,反之亦然。
  • 否则,如果你需要生成x个更多的y和y更多的零,下一个数字为1的几率为x /(x + y)。

答案 1 :(得分:2)

连续6个1并不是特别不可能 - 你确定你没有得到你想要的东西吗?

有一个简单的Python接口,用于均匀分布的随机数,那就是你要找的东西吗?

答案 2 :(得分:2)

您可以使用random.shuffle随机化列表。

import random
n = 100
seq = [0]*(n/2) + [1]*(n-n/2)
random.shuffle(seq)

现在你可以浏览列表,每当你看到一个太长的运行时,交换一个元素来分解序列。我还没有该部分的任何代码。

答案 3 :(得分:1)

可能不是最聪明的方式,但它适用于“无顺序运行”,而不会产生相同数量的0和1。请参阅下面的适用于所有要求的版本。

from random import choice
CHOICES = (1, 0)

def quasirandom(n, longest=3):
  serial = 0
  latest = 0
  result = []
  rappend = result.append

  for i in xrange(n):
    val = choice(CHOICES)
    if latest == val:
      serial += 1
    else:
      serial = 0
    if serial >= longest:
      val  = CHOICES[val]
    rappend(val)
    latest = val
  return result

print quasirandom(10)
print quasirandom(100)

下面的这个更正了过滤洗牌的想法,并正确地使用AFAICT,但需要注意的是最后一个数字可能会形成一个运行。通过debug=True检查是否符合要求。

from random import random
from itertools import groupby # For testing the result
try: xrange
except: xrange = range

def generate_quasirandom(values, n, longest=3, debug=False):
  # Sanity check
  if len(values) < 2 or longest < 1:
    raise ValueError

  # Create a list with n * [val]
  source = []
  sourcelen = len(values) * n
  for val in values:
    source += [val] * n

  # For breaking runs
  serial = 0
  latest = None

  for i in xrange(sourcelen):
    # Pick something from source[:i]
    j = int(random() * (sourcelen - i)) + i
    if source[j] == latest:
      serial += 1
      if serial >= longest:
        serial = 0
        guard = 0
        # We got a serial run, break it
        while source[j] == latest:
          j = int(random() * (sourcelen - i)) + i
          guard += 1
          # We just hit an infinit loop: there is no way to avoid a serial run
          if guard > 10:
            print("Unable to avoid serial run, disabling asserts.")
            debug = False
            break
    else:
      serial = 0
    latest = source[j]
    # Move the picked value to source[i:]
    source[i], source[j] = source[j], source[i]

  # More sanity checks
  check_quasirandom(source, values, n, longest, debug)

  return source


def check_quasirandom(shuffled, values, n, longest, debug):
  counts = []
  # We skip the last entries because breaking runs in them get too hairy
  for val, count in groupby(shuffled):
    counts.append(len(list(count)))
  highest = max(counts)
  print('Longest run: %d\nMax run lenght:%d' % (highest, longest))

  # Invariants
  assert len(shuffled) == len(values) * n
  for val in values:
    assert shuffled.count(val) == n

  if debug:
    # Only checked if we were able to avoid a sequential run >= longest
    assert highest <= longest

for x in xrange(10, 1000):
  generate_quasirandom((0, 1, 2, 3), 1000, x//10, debug=True)

答案 4 :(得分:1)

这是我的看法。前两个函数是实际的实现,最后一个函数是用于测试它。

键是第一个查看列表中最后N个元素的函数,其中N+1是您想要一个数字出现在一行中的次数的限制。它计算出现的数量,然后以(1 - N / n)概率返回1,其中n是已存在的数量。请注意,在N连续的情况下,此概率为0,在N个连续零的情况下,概率为1。

与真正的随机选择一样,无法保证1和0的比率为1,但在数千次运行中平均,它确实产生的数量为零。 对于较长的列表,这比重复调用shuffle并检查它是否满足您的要求要好。

import random


def next_value(selected):

    # Mathematically, this isn't necessary but it accounts for
    # potential problems with floating point numbers.
    if selected.count(0) == 0:
        return 0
    elif selected.count(1) == 0:
        return 1

    N = len(selected)
    selector = float(selected.count(1)) / N

    if random.uniform(0, 1) > selector:
        return 1
    else:
        return 0


def get_sequence(N, max_run):
    lim = min(N, max_run - 1)
    seq = [random.choice((1, 0)) for _ in xrange(lim)]

    for _ in xrange(N - lim):
        seq.append(next_value(seq[-max_run+1:]))
    return seq


def test(N, max_run, test_count):
    ones = 0.0
    zeros = 0.0

    for _ in xrange(test_count):
        seq = get_sequence(N, max_run)

        # Keep track of how many ones and zeros we're generating
        zeros += seq.count(0)
        ones += seq.count(1)

        # Make sure that the max_run isn't violated.
        counts = [0, 0]
        for i in seq:
            counts[i] += 1
            counts[not i] = 0
            if max_run in counts:
                print seq
                return


    # Print the ratio of zeros to ones. This should be around 1.
    print zeros/ones


test(200, 5, 10000)