【问题标题】:How can I iterate through every possible combination of n playing cards我如何遍历n张扑克牌的所有可能组合
【发布时间】:2011-02-22 10:04:30
【问题描述】:

如何在标准的 52 张纸牌中循环浏览所有 n 张纸牌的组合?

【问题讨论】:

    标签: c++ playing-cards


    【解决方案1】:

    您需要一组N 项目中n 项目的所有组合(在您的情况下为N == 52,但我将保持答案通用)。

    每个组合都可以表示为一个项目索引数组size_t item[n],这样:

    • 0 <= item[i] < N
    • item[i] < item[i+1],因此每个组合都是一个唯一的子集。

    以item[i] = i 开头。然后迭代到下一个组合:

    • 如果最终索引可以增加(即item[n-1] < N-1),那么就这样做。
    • 否则,向后工作直到找到可以递增的索引,并且仍然为所有以下索引留出空间(即item[n-i] < N-i)。增加它,然后将以下所有索引重置为可能的最小值。
    • 如果您找不到任何可以递增的索引(即item[0] == N-n),那么您就完成了。

    在代码中,它可能看起来有点像这样(未经测试):

    void first_combination(size_t item[], size_t n)
    {
        for (size_t i = 0; i < n; ++i) {
            item[i] = i;
        }
    }
    
    bool next_combination(size_t item[], size_t n, size_t N)
    {
        for (size_t i = 1; i <= n; ++i) {
            if (item[n-i] < N-i) {
                ++item[n-i];
                for (size_t j = n-i+1; j < n; ++j) {
                    item[j] = item[j-1] + 1;
                }
                return true;
            }
        }
        return false;
    }
    

    让它更通用,看起来更像std::next_permutation 可能会很好,但这是一般的想法。

    【讨论】:

      【解决方案2】:

      此组合迭代器类派生自此处发布的先前答案。

      我做了一些基准测试,它比您之前使用的任何 next_combination() 函数至少快 3 倍。

      我在 MetaTrader mql4 中编写了代码来测试外汇中的三角套利交易。我认为您可以轻松地将其移植到 Java 或 C++。

      class CombinationsIterator
      {
      private:
      	int input_array[];
      	int index_array[];
      	int m_indices;      // K
      	int m_elements;     // N
      
      public:
      	CombinationsIterator(int &src_data[], int k)
      	{
      		m_indices = k;
      		m_elements = ArraySize(src_data);
      		ArrayCopy(input_array, src_data);
      		ArrayResize(index_array, m_indices);
      
      		// create initial combination (0..k-1)
      		for (int i = 0; i < m_indices; i++)
      		{
      			index_array[i] = i;
      		}
      	}
      
      	// https://stackoverflow.com/questions/5076695
      	// bool next_combination(int &item[], int k, int N)
      	bool advance()
      	{
      		int N = m_elements;
      		for (int i = m_indices - 1; i >= 0; --i)
      		{
      			if (index_array[i] < --N)
      			{
      				++index_array[i];
      				for (int j = i + 1; j < m_indices; ++j)
      				{
      					index_array[j] = index_array[j - 1] + 1;
      				}
      				return true;
      			}
      		}
      		return false;
      	}
      
      	void get(int &items[])
      	{
      		// fill items[] from input array
      		for (int i = 0; i < m_indices; i++)
      		{
      			items[i] = input_array[index_array[i]];
      		}
      	}
      };
      
      //+------------------------------------------------------------------+
      //|                                                                  |
      //+------------------------------------------------------------------+
      // driver program to test above class
      
      #define N 5
      #define K 3
      
      void OnStart()
      {
      	int x[N] = {1, 2, 3, 4, 5};
      
      	CombinationsIterator comboIt(x, K);
      
      	int items[K];
      
      	do
      	{
      		comboIt.get(items);
      
      		printf("%s", ArrayToString(items));
      
      	} while (comboIt.advance());
      
      }

      输出:

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

      【讨论】:

        【解决方案3】:
        #include <iostream>
        #include <vector>
        using namespace std;
        
        class CombinationsIndexArray {
            vector<int> index_array;
            int last_index;
            public:
            CombinationsIndexArray(int number_of_things_to_choose_from, int number_of_things_to_choose_in_one_combination) {
                last_index = number_of_things_to_choose_from - 1;
                for (int i = 0; i < number_of_things_to_choose_in_one_combination; i++) {
                    index_array.push_back(i);
                }
            }
            int operator[](int i) {
                return index_array[i];
            }
            int size() {
                return index_array.size();
            }
            bool advance() {
        
                int i = index_array.size() - 1;
                if (index_array[i] < last_index) {
                    index_array[i]++;
                    return true;
                } else {
                    while (i > 0 && index_array[i-1] == index_array[i]-1) {
                        i--;
                    }
                    if (i == 0) {
                        return false;
                    } else {
                        index_array[i-1]++;
                        while (i < index_array.size()) {
                            index_array[i] = index_array[i-1]+1;
                            i++;
                        }
                        return true;
                    }
                }
            }
        };
        
        int main() {
        
            vector<int> a;
            a.push_back(1);
            a.push_back(2);
            a.push_back(3);
            a.push_back(4);
            a.push_back(5);
            int k = 3;
            CombinationsIndexArray combos(a.size(), k);
            do  {
                for (int i = 0; i < combos.size(); i++) {
                    cout << a[combos[i]] << " ";
                }
                cout << "\n";
            } while (combos.advance());
        
            return 0;
        }
        

        输出:

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

        【讨论】:

        • 尽管有一些非常清晰的变量名称,但这只是一堵没有上下文化或 cmets 的代码墙,因此是一个糟糕的答案。
        • 我不同意理查德的观点。使用“非常清晰的变量名”和代码结构使 cmets 变得不必要。避免不增加价值或提高理解的 cmets,因为它们很快就会与代码不同步。该解决方案非常易读,并且是一个 C++ 实现(由 Amr Ali 建议),它也演示了标准库的使用。
        【解决方案4】:

        我看到这个问题本质上与功率集问题相同。请参阅Problems with writing powerset code 以获得优雅的解决方案。

        【讨论】:

        • Powerset 枚举任意大小的所有子集。但这只需要大小为 n 的子集。这是一个组合问题,而不是幂集问题。
        猜你喜欢
        • 1970-01-01
        • 2016-02-24
        • 1970-01-01
        • 2011-04-19
        • 1970-01-01
        • 2021-05-17
        • 1970-01-01
        • 2013-01-17
        • 1970-01-01
        相关资源
        最近更新 更多