【问题标题】:Randomizing a list of zeros and ones with constraints随机化具有约束的零和一列表
【发布时间】:2020-06-15 19:47:04
【问题描述】:

我目前正在尝试随机化一个 0 和 1 列表,它应该给出一个随机顺序的 0 和具有以下约束的 1:

  1. 1/3 的项必须为 1(分别为 2/3 为 0)

  2. 连续出现的 1 不能超过两个

  3. 连续出现的零不得超过四个

我已经研究了一个选项,但结果并不完全是我需要的。这是我的选择:

for prevItem, nextItem in enumerate(WordV[: -1]):
            if nextItem  == WordV[prevItem+1] and WordV[prevItem+1] == WordV[prevItem+2] and nextItem ==1: 
                WordV[prevItem+2] = 0
            if nextItem  == WordV[prevItem+1] and WordV[prevItem+1] == WordV[prevItem+2] and WordV[prevItem+2] == WordV[prevItem+3] and WordV[prevItem+3] == WordV[prevItem+4] and nextItem == 0: 
                WordV[prevItem+2] = 1

# Check the number of ones & zeros
print(WordV)
ones= WordV.count(1)
zeros= WordV.count(0)
print(ones, zeros)

目前,1 和 0 的数量加起来不等于 1/3 到 2/3,因为约束替换了数字。 WordV 列表是一个包含 24 个 1 和 48 个 0 的列表,随机打乱(使用 random.shuffle(WordV))。

有没有更聪明(更正确)的方式将约束集成到代码中?

【问题讨论】:

  • 需要快速吗?

标签: python python-3.x list random shuffle


【解决方案1】:
import numpy as np

def consecutive(data, stepsize=0):
    return np.split(data, np.where(np.diff(data) != stepsize)[0]+1)

def check(list_to_check):
    groups = consecutive(list_to_check)
    for group in groups:
        if group[0] == 1 and group.size > 2:
            return True
        if group[0] == 0 and group.size > 4:
            return True

wordv = np.array([1]*24+[0]*48)


while check(wordv):
    np.random.shuffle(wordv)

wordv 将包含如下内容:

array([0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1,
       0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1,
       0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
       0, 0, 1, 0, 1, 0])

连续函数会将数据分成包含相同元素的组:

[ins] In [32]: consecutive([1,1,1,0,0,1])
Out[32]: [array([1, 1, 1]), array([0, 0]), array([1])]

检查将检查您指定的两个条件,我们将随机排列列表,直到我们满足条件

【讨论】:

  • 如果你只是重新洗牌整个列表直到你得到一个“好”的列表,如果列表有点长并且约束有点“紧”,这可能需要很长时间。
  • 要求是一个带有要求的随机列表,所以是的。遗传算法也可能找到解决方案,但这似乎有点过分了。每一种聪明的方法都太确定了,不能称它为随机的。
  • 对于短列表,如果这种方法可行,这实际上可能更随机,但对于较长的列表,它就不起作用,例如有 200 个 0 和 100 个 1。这可能不是问题,只是说。
  • 如果我有时间,我会检查遗传算法方法,它仍然应该返回一个随机结果,但根据定义可能并不总能找到一个。
【解决方案2】:

您可以尝试一种优化方法:从以正确比例保存元素的列表开始,然后不断交换随机元素,直到获得所需的结果。在每一轮中,检查过长的 0 或 1 的连续数,并始终保留原始列表或变异列表中较好的一个。

import itertools, random

def penalty(lst):
    return sum(1 for k, g in itertools.groupby(lst)
               if k == 0 and len(list(g)) > 4 or k == 1 and len(list(g)) > 2)

def constrained_shuffle(lst):
    # penalty of original list
    p = penalty(lst)
    while p > 0:
        # randomly swap two elements, get new penalty
        a, b = random.randrange(len(lst)), random.randrange(len(lst))
        lst[a], lst[b] = lst[b], lst[a]
        p2 = penalty(lst)
        if p2 > p:
            # worse than before, swap back
            lst[a], lst[b] = lst[b], lst[a]
        else:
            p = p2

lst = [0] * 20 + [1] * 10
random.shuffle(lst)
constrained_shuffle(lst)
print(lst)

对于 200 个 0 和 100 个 1,这将需要几百到几千次迭代,直到它找到一个有效的列表,这没关系。对于包含数千个元素的列表,这太慢了,但可能可以通过记住太长条纹的位置并最好在其中交换元素来改进。


关于这种方法的“随机性”:当然,它比重复生成一个新的随机列表直到一个符合约束条件的随机性要小,但我不明白这会如何产生对某些列表的偏见,只要满足约束条件。我做了一个简短的测试,反复生成随机列表并计算每个变体出现的频率:

counts = collections.Counter()
for _ in range(10000):
    lst = [0] * 10 + [1] * 5
    random.shuffle(lst)
    constrained_shuffle(lst)
    counts[tuple(lst)] += 1
print(collections.Counter(counts.values()).most_common())
[(7, 197), (6, 168), (8, 158), (9, 157), (5, 150), (10, 98), (4, 92), 
 (11, 81), (12, 49), (3, 49), (13, 43), (14, 23), (2, 20), (15, 10),
 (1, 8), (16, 4), (17, 3), (18, 1)]

所以,是的,也许有一些列表比其他列表更有可能(一个出现了 18 次,三个出现了 17 次,而其他大多数出现了 5-9 次)。对于 100,000 次迭代,“更有可能”列表的出现频率比其他列表高约 50%,但在这 100,000 次迭代中仍然只有大约 120 次,所以我认为这不是太大的问题。

如果没有最初的random.shuffle(lst),列表会比平均出现频率更高,因此不应跳过。

【讨论】:

  • @HeapOverflow 为什么不呢,请详细说明一下?它从一个随机打乱的列表开始,然后进行完全随机的替换,直到满足约束。
  • 你说开始“排序或正常洗牌,并不重要”。对于开始排序,我希望仍然存在偏见。对于开始洗牌,我不知道,但我可以想象它会产生偏见。
  • 我平均 sum(i for i, bit in enumerate(lst) if bit) 运行超过 10,000 次。开始洗牌时,平均在 145 左右。开始排序时,平均在 151 左右。始终如一。
  • 哦,这是一个论点:除非您的初始洗牌恰好有效(不太可能),否则您的结果只需一次交换即可无效。所以对这种“几乎无效”的结果存在偏见。
  • @HeapOverflow 我删除了关于初始洗牌是可选的位。如果没有改组,列表仍然是相当“随机”的,但对特定结果存在偏见。
【解决方案3】:

我不是很了解python,所以我给你伪代码:

int length;
int[] onesAndZeros = new int[length];

for(int i: onesAndZeros) { // generate a random list
    i = random(0, 1);
}

int zeroCount() { // correct the ratio
    int c;
    for(int i: onesAndZeros) {
        if(i == 0) {
            c++;
        }
    }
    return c;
}

int wantedZeros;
if(zeroCount() / (length - zeroCount()) != 2) { // you should probably check a small interval, but this answer is already long
    int a = 2*(length - zeroCount()) - zeroCount(); // I will include the math if necessary
    wantedZeros = zeroCount() + a;
}
while(zeroCount() != wantedZeros) {
    boolean isLess = zeroCount < wantedZeros;
    if(isLess) {
        onesAndZeros[random(0, length - 1)] = 0;
    } else {
        onesAndZeros[random(0, length - 1)] = 0;
    }
}

string isCorrect() { // fix the 2 1s and 4 0s
    for(int i = 0; i < length; i++) {
        if(onesAndZeros[i] == 0 &&
           onesAndZeros[i + 1] == 0 &&
           onesAndZeros[i + 2] == 0 &&
           onesAndZeros[i + 3] == 0 &&
           onesAndZeros[i + 4] == 0) { // be sure not to go out of bounds!
            return "0" + i;
        } else 
        if(onesAndZeros[i] == 1 &&
           onesAndZeros[i + 1] == 1 &&
           onesAndZeros[i + 2] == 1) {
            return "1" + i;
        } else {
            return "a";
        }
    }
}

void fix(int type, int idx) {
    if(type == 0) {
        onesAndZeros[idx + 4] = 1;
    } else {
        onesAndZeros[idx + 2] = 0;
    }
}

string corr = isCorrect();
while(length(corr) >= 2) { // note: this step will screw up the ones/zeros ratio a bit, if you want to restore it, consider running the last 2 steps again
    if(corr[0] == '0') {
        fix(0, toInt(removeFirstChar(corr)));
    } else {
        fix(1, toInt(removeFirstChar(corr)));
    }
}

// done!

我很清楚,这可以大大优化和清理,具体取决于语言。但这更像是一个坚实的基础。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-03
    • 2023-04-08
    • 2012-02-17
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-25
    相关资源
    最近更新 更多