【问题标题】:Algorithm to generate all possible arrays of ones and zeros of a given length生成给定长度的所有可能的 1 和 0 数组的算法
【发布时间】:2011-01-08 11:13:43
【问题描述】:

如何在长度为 n 的位数组中生成所有可能的位组合。如果我从数组中的所有零开始,则有 n 种可能性来放置第一位,对于这 n 种可能性,有 n-1 种可能性来放置第二位。单位所有 n 位都设置为 1。但到目前为止,我还没有设法将它编程出来。

还有很多人指出,我可以通过从 0 数到 (2^n)-1 并以二进制打印数字来做到这一点。这将是解决问题的一种简单方法,但是在这种情况下,我只是让机器计数而不是告诉它在哪里放置。我这样做是为了学习,所以我想知道如何编写一个放置方法。

【问题讨论】:

  • @Fred 如果我知道一个 lisp 答案和一个 C# 答案,我可以添加“[lisp]”和“[c#]”,还是应该将所有这些替换为“[language-agnostic] “?
  • @Johannes:我很想看到 LISP 和 C# 解决方案 :-) 我添加了 C++ 标签,因为 Nils 在 C++ 聊天中发布了这个问题,所以我假设他对 C++ 解决方案感兴趣。我添加了 Haskell 标签,以便真正的 Haskell 程序员可以改进我的 Haskell 解决方案。

标签: c++ algorithm language-agnostic haskell


【解决方案1】:

您将如何在纸上手动计数?你会检查最后一个数字。如果为 0,则将其设置为 1。如果已经为 1,则将其设置回 0 并继续下一个数字。所以这是一个递归过程。

以下程序通过改变一个序列来生成所有可能的组合:

#include <iostream>

template <typename Iter>
bool next(Iter begin, Iter end)
{
    if (begin == end)      // changed all digits
    {                      // so we are back to zero
        return false;      // that was the last number
    }
    --end;
    if ((*end & 1) == 0)   // even number is treated as zero
    {
        ++*end;            // increase to one
        return true;       // still more numbers to come
    }
    else                   // odd number is treated as one
    {
        --*end;            // decrease to zero
        return next(begin, end);   // RECURSE!
    }
}

int main()
{
    char test[] = "0000";
    do
    {
        std::cout << test << std::endl;
    } while (next(test + 0, test + 4));
}

该程序适用于任何类型的任何序列。如果您同时需要所有可能的组合,只需将它们放入集合中,而不是将它们打印出来。当然,您需要不同的元素类型,因为您不能将 C 数组放入向量中。让我们使用一个字符串向量:

#include <string>
#include <vector>

int main()
{
    std::vector<std::string> combinations;
    std::string test = "0000";
    do
    {
        combinations.push_back(test);
    } while (next(test.begin(), test.end()));
    // now the vector contains all pssible combinations
}

如果你不喜欢递归,这里有一个等效的迭代解决方案:

template <typename Iter>
bool next(Iter begin, Iter end)
{
    while (begin != end)       // we're not done yet
    {
        --end;
        if ((*end & 1) == 0)   // even number is treated as zero
        {
            ++*end;            // increase to one
            return true;       // still more numbers to come
        }
        else                   // odd number is treated as one
        {
            --*end;            // decrease to zero and loop
        }
    }
    return false;              // that was the last number
}

【讨论】:

  • 这个算法不是递归的,它只是迭代的。您可以直接跳回到开头,而不是递归——当然,使用推荐的控制结构 :-) 智能编译器可能会识别这一点,但可能不会。当然,运行时间以n为指数,堆栈增长只是线性的;所以该程序将起作用。但不必要的递归通常是一件坏事。
  • 非常好的算法...但是对这个算法有更好的解释吗..可能是一些幻灯片显示它的工作原理!
【解决方案2】:

这样的问题在功能上很容易解决。要找到长度为 n 的解,首先要找到长度为 n-1 的解,然后将“0”和“1”附加到这些解中,从而使解空间增加一倍。

这是一个简单的递归 Haskell 程序:

comb 0 = [[]]

comb n =
    let rest = comb (n-1)
    in  map ('0':) rest
     ++ map ('1':) rest

这是一个测试运行:

> comb 3
["000","001","010","011","100","101","110","111"]

【讨论】:

  • @sdcwc: +1 这正是我添加 Haskell 标签的原因 :)
  • 另外,mapM (\x -&gt; [0, 1]) [1..n](在我看来)更容易理解,它给出了长度为 n 位的所有排列。
  • 不使用单子组合符iterate (concatMap (\xs -&gt; [1:xs, 0:xs]) ) [[]]
【解决方案3】:

C++ 中“真正的”递归方法:

#include <iostream>
#include <string>

void print_digits(int n, std::string const& prefix = "") {
    if (!n) {
        std::cout << prefix << std::endl;
        return;
    }
    print_digits(n-1, prefix + '0');
    print_digits(n-1, prefix + '1');
}

int main(int, char**) {
    print_digits(4);
}

【讨论】:

    【解决方案4】:

    【讨论】:

      【解决方案5】:

      这是我的答案。优点是所有的组合都保存在一个二维数组中,但缺点是你只能用它来做一个长达 17 位的刺!!

      #include <iostream>
      
      using namespace std;
      
      int main()
          {
          long long n,i1=0,i2=0, i=1, j, k=2, z=1;
          cin >> n;
          while (i<n){
              k = 2*k;
              i++;
          }
          bool a[k][n], t = false;
          j = n-1;
          i1=0;
          i2 = 0;
          z = 1;
          while (j>=0){
              if(j!=n-1){
              z=z*2;
              }
              i2++;
              t = false;
              i = 0;
          while (i<k){
              i1 = 0;
              while (i1<z){
                  if(t==false){
                  a[i][j]=false;
                  }
                  else {
                      a[i][j]= true;
                  }
                  i1++;
                  i++;
              }
              if(t==false){
                  t = true;
              }else {
                  t = false;
              }
          }
          j--;
          }
          i = 0;
          j = 0;
          while (i<k){
              j = 0;
              while (j<n){
                  cout << a[i][j];
                  j++;
              }
              cout << endl;
              i++;
          }
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        FredOverflow 总体来说是对的。

        但是,对于 1 和 0,您最好只从 0 开始递增一个整数:

        int need_digits = 10
        unsigned int i=0
        while (! i>>need_digits){
            # convert to binary form: shift & check, make an array, or cast to string, anything.
            }
        

        ...我想您不需要超过 32 位,否则您必须链接多个整数..并坚持上一个答案:)

        【讨论】:

        • +1 以简化方法。但是,这种循环条件很严峻。请使用(for i = 0; i &lt; (1L &lt;&lt; need_digits); i++) 或类似名称!
        • 为了学习你最好尽量不要用1s&0s来限制任务,试着生成像[a..c][a..d][a..z]之类的数字;)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-04
        • 1970-01-01
        • 1970-01-01
        • 2012-02-28
        • 2021-12-14
        相关资源
        最近更新 更多