计算置换子集

Calculate Permutation Subsets

上下文: 我想解决纸牌游戏。为了减少可能的游戏计算,我使用信息抽象。这导致排列的子集。计算子集就是问题

示例: 给定 2 个玩家,每个玩家从一副 8 张牌中拿 2 张牌。有 1680 个排列。导致相同游戏的一些排列可以从游戏列表中删除以进行计算,例如: {1,2,3,4} , {1,2,4,3} , {2,1,3,4} , {2,1,4,3} 都会导致相同的游戏,因为这无关紧要玩家以何种顺序收到卡片,重要的是他得到了什么卡片。 所以我可以删除不满足的排列:{a,b,c,d} aFormula with slider to play around.

我的问题是找到一个算法来生成这个子集并且很灵活,这样我就可以在不改变的情况下从 8 中计算出 4 张牌,从 12 中计算出 6 张牌。

编辑: 目前我尝试将子集除以二,然后找到元素递增的所有排列,然后我尝试找到大小为 2

结果的所有排列

示例: 2 来自 {1,2,3,4,5,6,7,8} {1,2},{1,3}, {7,8} 将结果用于新排列,从忽略相等数字的所有结果中取 2。 可能的结果: {{1,2},{3,4}} {{3,4},{1,2}}

解决办法是: 将子集除以二然后求出所有元素递增的排列然后尝试求出结果的所有排列

示例: 4 来自 {1,2,3,4,5,6,7,8}

  1. 从 8 计算两个元素,得到 {1,2},{1,3} ... {7,8}
  2. 将结果用于新排列 {{1,2},{1,3}}, {{1,2},{3,4}}
  3. 为每个新排列创建一个数组 {1,2,1,3}, {1,2,3,4}
  4. 如果其中所有数字都是唯一的,则对其进行排序并将其推送到结果({1,2,1,3} 无效,因为它有两个)。

C++ 代码:(未优化)
旧子集

#include <vector>
#include <iostream>
// Requires: sequence from begin to end is sorted
//           middle is between begin and end
template <typename Bidi, typename Functor>
void
for_each_permuted_combination (Bidi begin, Bidi middle, Bidi end, Functor func)
{
  do
    {
      func (begin, middle);
      std::reverse (middle, end);
    }
  while (std::next_permutation (begin, end));
}

std::vector<std::vector<uint8_t> >
oldSubset (int subsetSize, std::vector<uint8_t> setOfNumbers)
{
  if (subsetSize % 2 != 0)
    {
      throw std::logic_error{ "only tested for even subsets" };
      return {};
    }
  auto subsets = std::vector<std::vector<uint8_t> >{};
  for_each_permuted_combination (setOfNumbers.begin (), setOfNumbers.begin () + subsetSize / 2, setOfNumbers.end (), [&subsets] (auto begin, auto end) {
    if (std::is_sorted (begin, end))
      {
        subsets.push_back (std::vector<uint8_t> (begin, end));
      }
  });
  auto permutations = std::vector<std::vector<uint8_t> >{};
  for_each_permuted_combination (subsets.begin (), subsets.begin () + 2, subsets.end (), [&permutations] (auto begin, auto end) {
    auto permutation = std::vector<std::vector<uint8_t> > (begin, end);
    auto possibleResult = std::vector<uint8_t>{};
    std::copy (permutation.front ().begin (), permutation.front ().end (), std::back_inserter (possibleResult));
    std::copy (permutation.back ().begin (), permutation.back ().end (), std::back_inserter (possibleResult));
    auto copyOfPossibleResult = possibleResult;
    std::sort (copyOfPossibleResult.begin (), copyOfPossibleResult.end ());
    if (std::adjacent_find (copyOfPossibleResult.begin (), copyOfPossibleResult.end ()) == copyOfPossibleResult.end ())
      {
        permutations.push_back (possibleResult);
      }
  });
  return permutations;
}

int
main ()
{
  auto results = oldSubset (4, { 1, 2, 3, 4, 5, 6});
    for(auto const& result:results){
        std::copy (result.begin (), result.end (), std::ostream_iterator<int> (std::cout, " "));
        std::cout<<std::endl;
    }
}

输出

1 2 3 4 
1 2 3 5 
1 2 3 6 
1 2 4 5 
1 2 4 6 
1 2 5 6 
1 3 2 4 
1 3 2 5 
1 3 2 6 
1 3 4 5 
1 3 4 6 
1 3 5 6 
1 4 2 3 
1 4 2 5 
1 4 2 6 
1 4 3 5 
1 4 3 6 
1 4 5 6 
1 5 2 3 
1 5 2 4 
1 5 2 6 
1 5 3 4 
1 5 3 6 
1 5 4 6 
1 6 2 3 
1 6 2 4 
1 6 2 5 
1 6 3 4 
1 6 3 5 
1 6 4 5 
2 3 1 4 
2 3 1 5 
2 3 1 6 
2 3 4 5 
2 3 4 6 
2 3 5 6 
2 4 1 3 
2 4 1 5 
2 4 1 6 
2 4 3 5 
2 4 3 6 
2 4 5 6 
2 5 1 3 
2 5 1 4 
2 5 1 6 
2 5 3 4 
2 5 3 6 
2 5 4 6 
2 6 1 3 
2 6 1 4 
2 6 1 5 
2 6 3 4 
2 6 3 5 
2 6 4 5 
3 4 1 2 
3 4 1 5 
3 4 1 6 
3 4 2 5 
3 4 2 6 
3 4 5 6 
3 5 1 2 
3 5 1 4 
3 5 1 6 
3 5 2 4 
3 5 2 6 
3 5 4 6 
3 6 1 2 
3 6 1 4 
3 6 1 5 
3 6 2 4 
3 6 2 5 
3 6 4 5 
4 5 1 2 
4 5 1 3 
4 5 1 6 
4 5 2 3 
4 5 2 6 
4 5 3 6 
4 6 1 2 
4 6 1 3 
4 6 1 5 
4 6 2 3 
4 6 2 5 
4 6 3 5 
5 6 1 2 
5 6 1 3 
5 6 1 4 
5 6 2 3 
5 6 2 4 
5 6 3 4 

编辑:优化版本
子集

#include <vector>
#include <tuple>
#include <iostream>
#include <numeric>
typedef std::tuple<std::vector<u_int8_t>, std::vector<std::vector<u_int8_t> > > subsetAndCombinations;

// Requires: sequence from begin to end is sorted
//           middle is between begin and end
template <typename Bidi, typename Functor>
void
for_each_permuted_combination (Bidi begin, Bidi middle, Bidi end, Functor func)
{
  do
    {
      func (begin, middle);
      std::reverse (middle, end);
    }
  while (std::next_permutation (begin, end));
}

std::vector<std::vector<u_int8_t> >
subsetPermutation (long int subsetSize, std::vector<uint8_t> setOfNumbers)
{
  if (subsetSize % 2 != 0) return {};
  std::vector<std::vector<u_int8_t> > subsets{};
  for_each_permuted_combination (setOfNumbers.begin (), setOfNumbers.begin () + subsetSize / 2, setOfNumbers.end (), [&subsets] (auto begin, auto end) mutable {
    if (std::is_sorted (begin, end))
      {
        subsets.push_back ({ begin, end });
      }
  });
  return subsets;
}


subsetAndCombinations
combinationsFor (std::vector<u_int8_t> const &numbersToCheck, std::vector<std::vector<u_int8_t> > const &subResults, std::vector<u_int8_t> const &numbersToChoseFrom)
{
  auto result = std::tuple<std::vector<u_int8_t>, std::vector<std::vector<u_int8_t> > >{};
  std::get<0> (result) = numbersToCheck;
  std::get<1> (result).reserve (subResults.size ());
  std::vector<u_int8_t> numbers (numbersToChoseFrom.size () - numbersToCheck.size ());
  std::set_difference (numbersToChoseFrom.begin (), numbersToChoseFrom.end (), numbersToCheck.begin (), numbersToCheck.end (), numbers.begin ());
  std::transform (subResults.begin (), subResults.end (), std::back_inserter (std::get<1> (result)), [&numbers] (auto indexes) {
    std::transform (indexes.begin (), indexes.end (), indexes.begin (), [&numbers] (auto index) { return numbers[index]; });
    return indexes;
  });
  return result;
}

std::vector<subsetAndCombinations>
subset (long int k, size_t n)
{
  auto indexes = std::vector<uint8_t> (n);
  std::iota (indexes.begin (), indexes.end (), 0);
  auto results = subsetPermutation (k, indexes);
  auto subResult = subsetPermutation (k, std::vector<uint8_t> (indexes.begin (), indexes.begin () + static_cast<long int> (n) - (k / 2)));
  auto combineResult = std::vector<subsetAndCombinations>{};
  for (auto result : results)
    {
      combineResult.push_back (combinationsFor (result, subResult, indexes));
    }
  return combineResult;
}


int
main ()
{
  auto results = subset (4, 6);
  //print results
  for (auto const &[subset, combis] : results)
    {
      for (auto const &combi : combis)
        {
          std::copy (subset.begin (), subset.end (), std::ostream_iterator<int> (std::cout, " "));
          std::copy (combi.begin (), combi.end (), std::ostream_iterator<int> (std::cout, " "));
          std::cout << std::endl;
        }
      std::cout << std::endl;
    }
}

edit2:oldSubset 与在 Intel(R) Core(TM) i5-8600K 上使用 catch2 的子集的基准测试 CPU @ 3.60GHz
clang 版本 13.0.0 发布版本

benchmark name                       samples       iterations    estimated
                                     mean          low mean      high mean
                                     std dev       low std dev   high std dev
-------------------------------------------------------------------------------
subset (4, sixNumbers)                         100             8     4.2488 ms 
                                        5.12302 us    5.11219 us    5.15303 us 
                                        85.5219 ns    37.5917 ns    183.126 ns 
                                                                               
oldSubset (4, sixNumbers)                      100             1     5.3749 ms 
                                        54.1088 us    53.6284 us    55.3097 us 
                                        3.60139 us    1.65755 us    7.04574 us 
                                                                               
subset (4, eightNumbers)                       100             2     4.1362 ms 
                                        20.6127 us    20.3747 us     21.221 us 
                                        1.80225 us    845.815 ns    3.48066 us 
                                                                               
oldSubset (4, eightNumbers)                    100             1    24.4414 ms 
                                        237.982 us     236.57 us    240.066 us 
                                        8.63522 us    6.29346 us    12.1139 us 
                                                                               
subset (6, eightNumbers)                       100             1      4.107 ms 
                                        41.1901 us     41.005 us    41.7309 us 
                                        1.48208 us    634.215 ns    3.21124 us 
                                                                               
oldSubset (6, eightNumbers)                    100             1    149.468 ms 
                                        1.49836 ms    1.49526 ms    1.50226 ms 
                                        17.5873 us    14.9794 us    22.8566 us 
                                                                               
subset (4, tenNumbers)                         100             1     6.5134 ms 
                                        65.0695 us    64.5343 us    65.9829 us 
                                         3.4798 us    2.28411 us    4.82172 us 
                                                                               
oldSubset (4, tenNumbers)                      100             1    74.4154 ms 
                                         754.08 us    752.067 us     756.94 us 
                                        12.1161 us    9.50739 us     15.514 us 
                                                                               
subset (6, tenNumbers)                         100             1    21.2435 ms 
                                        209.317 us    207.847 us    211.412 us 
                                        8.88665 us    6.72703 us    11.1798 us 
                                                                               
oldSubset (6, tenNumbers)                      100             1     1.17232 s 
                                        11.7905 ms    11.7811 ms    11.8024 ms 
                                        53.7095 us    43.4715 us    65.5498 us 
                                                                               
subset (8, tenNumbers)                         100             1    23.4467 ms 
                                        235.972 us    234.262 us    238.712 us 
                                        10.8037 us    7.82671 us    16.3172 us 
                                                                               
oldSubset (8, tenNumbers)                      100             1     6.65554 s 
                                        66.7037 ms    66.5035 ms    66.9362 ms 
                                        1.09843 ms    954.738 us    1.22926 ms 
                                                                               
subset (4, twelveNumbers)                      100             1    14.8182 ms 
                                        149.715 us    148.325 us    152.211 us 
                                        9.22947 us    5.93872 us    14.6366 us 
                                                                               
oldSubset (4, twelveNumbers)                   100             1     204.43 ms 
                                        2.06251 ms    2.05898 ms    2.06644 ms 
                                        18.9013 us    14.9089 us    25.4169 us 
                                                                               
subset (6, twelveNumbers)                      100             1    85.5855 ms 
                                        841.024 us    837.534 us    845.313 us 
                                        19.7371 us    16.5515 us    24.1813 us 
                                                                               
oldSubset (6, twelveNumbers)                   100             1     6.65107 s 
                                        67.0061 ms    66.9113 ms    67.1053 ms 
                                        495.839 us    452.768 us    552.615 us 
                                                                               
subset (8, twelveNumbers)                      100             1    176.525 ms 
                                        1.76037 ms    1.75612 ms    1.76481 ms 
                                        22.2971 us     20.032 us    25.2517 us 
                                                                               
oldSubset (8, twelveNumbers)                   100             1     1.36774 m 
                                          819.4 ms    817.476 ms     821.68 ms 
                                        10.7189 ms    9.24014 ms    12.3794 ms 
                                                                               
subset (10, twelveNumbers)                     100             1    196.563 ms 
                                        1.94786 ms    1.93109 ms    1.96496 ms 
                                         86.618 us    77.6169 us    96.7013 us 
                                                                               
oldSubset (10, twelveNumbers)                  100             1     6.28968 m 
                                         3.83712 s     3.83005 s     3.84504 s 
                                        38.1357 ms    33.2592 ms    44.7012 ms 

g++ (GCC) 11.1.0 发布版本

benchmark name                       samples       iterations    estimated
                                     mean          low mean      high mean
                                     std dev       low std dev   high std dev
-------------------------------------------------------------------------------
subset (4, sixNumbers)                         100             5     2.2055 ms 
                                        4.39944 us    4.38703 us    4.44751 us 
                                        106.292 ns    11.2639 ns    244.087 ns 
                                                                               
oldSubset (4, sixNumbers)                      100             1     5.2325 ms 
                                        51.7199 us    51.6355 us    51.9248 us 
                                        628.408 ns    315.255 ns    1.25787 us 
                                                                               
subset (4, eightNumbers)                       100             2      3.473 ms 
                                        17.2042 us    17.0591 us    17.8612 us 
                                         1.3454 us     169.46 ns    3.18688 us 
                                                                               
oldSubset (4, eightNumbers)                    100             1     24.246 ms 
                                        243.944 us    243.561 us    244.451 us 
                                        2.23092 us    1.79177 us    2.77771 us 
                                                                               
subset (6, eightNumbers)                       100             1     3.6771 ms 
                                        37.6169 us    37.4309 us    38.1215 us 
                                        1.44523 us    666.679 ns    3.09803 us 
                                                                               
oldSubset (6, eightNumbers)                    100             1    145.011 ms 
                                        1.40323 ms    1.40017 ms    1.40992 ms 
                                        22.0044 us    11.2033 us    39.7721 us 
                                                                               
subset (4, tenNumbers)                         100             1     5.7933 ms 
                                        58.8654 us    58.6273 us    59.5799 us 
                                         1.9226 us    788.588 ns    4.18631 us 
                                                                               
oldSubset (4, tenNumbers)                      100             1    79.7406 ms 
                                        793.094 us    792.263 us    794.172 us 
                                        4.80223 us    3.88654 us    5.93634 us 
                                                                               
subset (6, tenNumbers)                         100             1    18.3623 ms 
                                        188.659 us    188.158 us    190.088 us 
                                        3.97281 us    1.77668 us    8.57928 us 
                                                                               
oldSubset (6, tenNumbers)                      100             1     1.16914 s 
                                        11.6407 ms    11.6321 ms    11.6509 ms 
                                        47.6677 us    39.6378 us    60.7322 us 
                                                                               
subset (8, tenNumbers)                         100             1    22.1842 ms 
                                        222.242 us    221.612 us    223.478 us 
                                        4.32973 us     2.0686 us    8.21289 us 
                                                                               
oldSubset (8, tenNumbers)                      100             1       6.398 s 
                                        63.9016 ms    63.8632 ms    63.9403 ms 
                                        196.147 us    177.473 us    221.609 us 
                                                                               
subset (4, twelveNumbers)                      100             1    12.2964 ms 
                                        124.878 us    124.642 us    125.304 us 
                                        1.57641 us    1.02773 us    2.91879 us 
                                                                               
oldSubset (4, twelveNumbers)                   100             1    219.962 ms 
                                        2.18378 ms     2.1825 ms    2.18628 ms 
                                        8.76462 us    5.40766 us    16.4105 us 
                                                                               
subset (6, twelveNumbers)                      100             1    74.5191 ms 
                                        749.617 us    748.214 us    753.058 us 
                                        10.4827 us    5.42864 us    21.6588 us 
                                                                               
oldSubset (6, twelveNumbers)                   100             1     6.53912 s 
                                        65.2901 ms    65.2766 ms    65.3227 ms 
                                        100.232 us    50.7812 us    201.115 us 
                                                                               
subset (8, twelveNumbers)                      100             1     157.06 ms 
                                        1.58509 ms    1.58038 ms    1.59278 ms 
                                        30.1286 us    21.3724 us    49.5146 us 
                                                                               
oldSubset (8, twelveNumbers)                   100             1     1.30347 m 
                                        775.358 ms    773.562 ms    776.915 ms 
                                        8.46007 ms    7.32211 ms    9.60251 ms 
                                                                               
subset (10, twelveNumbers)                     100             1    189.041 ms 
                                        1.89293 ms    1.88865 ms    1.90194 ms 
                                        30.2312 us    15.9272 us    58.5911 us 
                                                                               
oldSubset (10, twelveNumbers)                  100             1     5.27331 m 
                                         3.14281 s     3.13666 s     3.14754 s 
                                        27.4681 ms    22.0504 ms    33.0537 ms 

这是 Python 中的解决方案 - 抱歉,没有时间用 C++ 编写它。 foo 生成器计算纸牌总数和发给玩家的纸牌总和,假设每个玩家收到的纸牌数量相同。然后它会在每次迭代时生成下一个组合。

del_elmtsrestore_elmts 函数只是为了避免在从牌组中移除玩家 1 的手时将整个数组 P 复制到临时数组中。警告:Python 仍然通过 [:n-k//2] 范围访问生成几乎完整的副本,但在 C++ 中,您可以通过使组合函数更智能一些来避免这种情况。

import itertools

def del_elmts(li,delset):
    for i in range(len(delset)-1,-1,-1):
        if (delset[i] < len(li)-len(delset)):
            li[delset[i]] = li[len(li)-len(delset)+i]

def restore_elmts(li,delset):
    for i in delset:
        li[i] = i

def foo(n,k):
    if k % 2 != 0: raise ValueError
    P = list(range(n))
    for p in itertools.combinations(P,k//2):
        del_elmts(P,p)
        for p2 in itertools.combinations(P[:n-k//2],k//2):
            yield p+p2
        restore_elmts(P,p)

            
        


x = foo(6,4)
for y in x:
    print(list(map(lambda x: x+1,y)))

PS: 如果你想将它与另一个算法进行比较,你可能需要对输出的前半部分和后半部分进行排序。