【问题标题】:Generating all possible sets of 3 groups of 6 numbers chosen from the integers 1-18生成从整数 1-18 中选择的 3 组 6 个数字的所有可能集合
【发布时间】:2020-10-08 23:48:52
【问题描述】:

由于 6 组代表骰子,因此这些组中整数的顺序无关紧要。每组中组的顺序确实很重要,但要检查每个组,找到每组的排列很简单,我可以做到。

现在,我想出了一个方法来实现这一切,问题是效率。我想检查每一组,但到目前为止我只想出了一种方法:

for i in itertools.combinations(num, 6):
    W.append([i])
print(W)
print("Done building list of ",len(W)," possible dice.")

这给了我从 18 个整数中选择的 6 个数字的所有可能骰子,其中数字的顺序无关紧要。这是 18,564 组,每组 6 个。然后,我使用以下方法找到这些骰子的组合:

for j in itertools.combinations(W, 3):
    check = list(itertools.chain(*list(itertools.chain(*j))))
    check = sorted(check)
#print(check)
if check == num:
   

问题在于第二个组合将迭代 10^15 种可能性,而我想要生成的只是其中的一小部分。

我还有一个问题:

我很确定将 18 个事物分配给 3 个不同的 6 个组的总方法,其中组内的顺序无关紧要:18!/(6!)^3 ~ 1700 万种方式,可以相对快速地迭代。这是正确的吗?有没有一种方法可以迭代地生成这些方式,而无需过滤更多的可能性?

最后一点:

我正在尝试生成所有非传递六面骰子集,其中每个骰子的每个面都有不同的数字。我已经有代码可以检查一组并确定它是否符合此条件并存储最佳结果,即骰子击败下一个骰子的最高概率。

 for k in itertools.permutations(j):
        B = k[0]
        G = k[1]
        R = k[2]

        b = 0
        g = 0
        r = 0

        for m in B:
            for n in G:
                if m > n:
                    b = b + 1
                else:
                    continue
        for m in G:
            for n in R:
                if m > n:
                    g = g + 1
                else:
                    continue
        for m in R:
            for n in B:
                if m > n:
                    r = r + 1
                else:
                    continue

        w = w + 1
        print(w)
        if b <= 18 or g <= 18 or r <= 18:
            continue
        else:
            if b >= blue and g >= green  and r >= red:
                Blue = B
                blue = b

                Green = G
                green = g

                Red = R
                red = r

                print(B)
                print(b/36)
                print(G)
                print(g/36)
                print(R)
                print(r/36)
                continue
            else:
                continue
else:
    continue

【问题讨论】:

  • 这和Partitions of Groups of Equal Size的想法一样吗?
  • 哦,哇,这看起来很有希望,谢谢。我尝试了很长时间在这个网站和其他网站上搜索这个问题,但它是关于找到正确的关键词。我会看看这个,谢谢。另外,我可以在python中轻松实现吗?我有点像新手,尤其是在涉及不同语言和实现库等方面。

标签: python set combinations permutation


【解决方案1】:

正如 cmets 中所述,OP 似乎正在寻找 Partitions of Groups of Equal Size

下面的代码是从C++ 中实现的算法转换而来的:https://github.com/jwood000/RcppAlgos/blob/master/src/ComboGroupsUtils.cpp*。我将注释放在下面自己的一个块中,然后是 python 化的代码(注意,这是直接翻译,可能还有改进的余地)。

********算法关键部分概述********


last1是上一节中的上界加一,所以要得到当前当前的上界,我们必须先加上一节的大小(即grpSize)再减一。我们现在可以通过减去idx1 来计算重置v 所需的长度。例如

给定vgrpSize = 4idx1 = 9、6 个组(24 个科目)和基数 0 的一部分:

         prev sections   bound (index = 8)
             /  \        |
       ............. 8 | 9 12 23 24 | 10 20 21 22 | 11 ... 
                            |
                          idx1 (equal to last1, in this case)

排序v 过去idx1

                 ... 8 | 9 12 10 11 | 13 14 15 16 | 17... 

确定索引idx3,使得v[idx3] > v[idx1]

                 ... 8 | 9 12 10 11 | 13 14 15 16 | 17 ... 
                            |          |
                          idx1       idx3

交换idx1idx3

                 ... 8 | 9 13 10 11 | 12 14 15 16 | 17... 

idx1 之后移动足够的索引以填充该特定组:

                 ... 8 | 9 13 __ __ | 10 11 12 14 | 15 16 ... 

识别并移动将v 的值连续递增到idx1 之后的索引:

                 ... 8 | 9 13 14 15 | 10 11 12 16 | 17 ... 

最后两个步骤通过std::rotate 完成。这样就完成了算法。

这里有一些辅助函数:

def rotate(l, n):
    return l[n:] + l[:n]

def numGroupCombs(n, nGrps, grpSize):
    result = 1

    for i in range(n, nGrps, -1):
        result *= i

    result = int(result)
    myDiv = 1

    for i in range(2, grpSize + 1):
        myDiv *= i

    result /= myDiv**nGrps
    return int(result)

这里是主生成器(旋转函数是从这个答案Python list rotation获得的):

def ComboGroups(v, nGrps, grpSize):

    if not isinstance(v, list):
        z = list(v)
    else:
        z = v.copy()

    for i in range(numGroupCombs(len(z), nGrps, grpSize)):
        yield z.copy()

        idx1 = (nGrps - 1) * grpSize - 1
        idx2 = len(z) - 1;
        last1 = (nGrps - 2) * grpSize + 1

        while (idx2 > idx1 and z[idx2] > z[idx1]):
            idx2 -= 1

        if (idx2 + 1) < len(z):
            if z[idx2 + 1] > z[idx1]:
                z[idx1], z[idx2 + 1] = z[idx2 + 1], z[idx1]
        else:
            while idx1 > 0:
                tipPnt = z[idx2]

                while (idx1 > last1 and tipPnt < z[idx1]):
                    idx1 -= 1

                if tipPnt > z[idx1]:
                    idx3 = idx1 + 1
                    z[idx3:] = sorted(z[idx3:])
                    xtr = last1 + grpSize - idx3

                    while z[idx3] < z[idx1]:
                        idx3 += 1

                    z[idx3], z[idx1] = z[idx1], z[idx3]
                    z[(idx1 + 1):(idx3 + xtr)] = rotate(z[(idx1 + 1):(idx3 + xtr)], idx3 - idx1)
                    break
                else:
                    idx1 -= 2
                    idx2 -= grpSize
                    last1 -= grpSize

这是一个示例用法 (https://ideone.com/kygF03):

import time

def example(z, nGrps, verbose = True):
    grpSize = int(len(z) / nGrps)

    if verbose:
        for a in ComboGroups(z, nGrps, grpSize):
            print([a[i:i + grpSize] for i in range(0, len(a), grpSize)])
    else:
        start = time.time()

        for a in ComboGroups(z, nGrps, grpSize):
            b = a

        end = time.time()
        print(end - start)

example(list(range(1, 9)), 2)
[[1, 2, 3, 4], [5, 6, 7, 8]]
[[1, 2, 3, 5], [4, 6, 7, 8]]
[[1, 2, 3, 6], [4, 5, 7, 8]]
[[1, 2, 3, 7], [4, 5, 6, 8]]
[[1, 2, 3, 8], [4, 5, 6, 7]]
[[1, 2, 4, 5], [3, 6, 7, 8]]
[[1, 2, 4, 6], [3, 5, 7, 8]]
[[1, 2, 4, 7], [3, 5, 6, 8]]
[[1, 2, 4, 8], [3, 5, 6, 7]]
[[1, 2, 5, 6], [3, 4, 7, 8]]
[[1, 2, 5, 7], [3, 4, 6, 8]]
[[1, 2, 5, 8], [3, 4, 6, 7]]
[[1, 2, 6, 7], [3, 4, 5, 8]]
[[1, 2, 6, 8], [3, 4, 5, 7]]
[[1, 2, 7, 8], [3, 4, 5, 6]]
[[1, 3, 4, 5], [2, 6, 7, 8]]
[[1, 3, 4, 6], [2, 5, 7, 8]]
[[1, 3, 4, 7], [2, 5, 6, 8]]
[[1, 3, 4, 8], [2, 5, 6, 7]]
[[1, 3, 5, 6], [2, 4, 7, 8]]
[[1, 3, 5, 7], [2, 4, 6, 8]]
[[1, 3, 5, 8], [2, 4, 6, 7]]
[[1, 3, 6, 7], [2, 4, 5, 8]]
[[1, 3, 6, 8], [2, 4, 5, 7]]
[[1, 3, 7, 8], [2, 4, 5, 6]]
[[1, 4, 5, 6], [2, 3, 7, 8]]
[[1, 4, 5, 7], [2, 3, 6, 8]]
[[1, 4, 5, 8], [2, 3, 6, 7]]
[[1, 4, 6, 7], [2, 3, 5, 8]]
[[1, 4, 6, 8], [2, 3, 5, 7]]
[[1, 4, 7, 8], [2, 3, 5, 6]]
[[1, 5, 6, 7], [2, 3, 4, 8]]
[[1, 5, 6, 8], [2, 3, 4, 7]]
[[1, 5, 7, 8], [2, 3, 4, 6]]
[[1, 6, 7, 8], [2, 3, 4, 5]]

对于您的示例,上面未优化的实现可以在 4 秒多的时间内遍历所有 2,858,856 结果。

example(list(range(1, 19)), 3, False)
4.4308202266693115

作为参考,C++ 中的相同算法运行时间约为 0.12 秒。

*我是RcppAlgos的作者

【讨论】:

  • 非常感谢您!我试图理解算法是如何工作的,既然你说我的例子有大约 200 万个结果,我假设它删除了一组骰子的排列。因此,我为算法生成的每一组添加了一个 for 循环来遍历这些,但在我的结果中,我似乎得到了 6 个正在生成的相同骰子。我不确定我在这里做错了什么。
  • 没关系,我的代码中出现了一些错误,非常感谢您的回复,它确实有效。
【解决方案2】:

首先,从 18 种事物中选择 6 种事物的所有组合。这些是你的第一个骰子。有 18564 种可能性。

然后,类似地,从剩余的 12 个 (924) 中选择 6 个的所有组合。这会给你第二个骰子,给你第一个。

最后,第三个骰子就是剩下的所有数字。那是 18564x924x1=17,153,136 种可能性。

但实际上,等等。我们也不关心三个骰子的顺序。所以我们可以假设“1”在第一个骰子上。然后不是第一个骰子的最小数字是第二个骰子。那是 6188x462x1=2,858,856 种可能性。

这是一些 python 代码。它不像你自己组合那样快,但它应该运行良好。

如果您在真正的传递骰子上运行它,请尝试删除“唯一”约束!我很好奇是否有有趣的重复骰子。

import itertools
def iter_dice():
    nums = list(range(1,19))
    for first_die in itertools.combinations(nums[1:], 5):
        first_die = (1,) + first_die
        remaining = sorted(set(nums) - set(first_die))
        for second_die in itertools.combinations(remaining[1:], 5):
            second_die = (remaining[0],) + second_die
            third_die = sorted(set(remaining) - set(second_die))
            yield first_die, second_die, third_die
print(len(list(iter_dice())))
print(next(iter_dice()))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 2018-01-06
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    相关资源
    最近更新 更多