【问题标题】:How to generate all n-digit number in the n-digit-array way?如何以 n 位数组的方式生成所有 n 位数字?
【发布时间】:2023-03-03 08:41:26
【问题描述】:

我知道如何以传统的数字方式生成所有n位数字,

for(long long number = pow(10, n-1); number < pow(10, n); number++) {
    cout << number << endl; 
}

例如,

for n = 5, it will generate 10000 to 99999;

但是,由于我必须计算每个数字的数字,所以首先以数字数组格式构造数字会很方便。

例如,以下代码以数组方式生成所有 5 位数字:

for(int i = 1; i < 9; i++)
    for(int j = 0; j < 9; j++)
        for(int k = 0; k < 9; k++)
            for(int l = 0; l < 9; l++)
                for(int m = 0; m < 9; m++) {
                    //executed 9 * 10^4 = 90000 times
                    //construct my array instance with i, j, k, l, m
                    cout << i << j << k << l << m << endl;      
                }

现在的问题是:n 是未知的。 (例如,它可以是 2、3、4、5、6...、10) 那么如何根据数字 n 生成 n 位数组呢?

例如,我想要一段如下代码(任何比这个更好的方法都非常感谢):

for(int x = 0; x < n; x++) {
    //each x is a layer of the loop ?!
    .....

}

【问题讨论】:

  • 我建议只存储数字数组而不是存储所有数字的每个数字。当您要处理特定数字时,只需使用这样的语句:while(n) { printf("%d", n % 10); n /= 10;}
  • @prehistoricpenguin 为什么?将每个数字上的语句转换为数字数组会比类似排列的解决方案更快吗?
  • 为了更少的内存消耗,如果只遍历一次数组,没有效率问题。
  • @prehistoricpenguin 我做了一个实验,并在 Trevor 的帖子中写了评论。

标签: c++ algorithm


【解决方案1】:

不考虑效率的简单方法:

#include <cstdio>

int main(void) {
    int n = 3; // the number of digits
    long long start = 1;
    int *array = new int[n];
    for (int i = 1; i < n; i++) start *= 10;
    for(long long x = start; x < start * 10; x++) { // not all 10-digit number will fit in 32-bit integer
        // get each digits in decimal, lowest digit in array[0]
        for (int i = 0, shift = 1; i < n; i++, shift *= 10) array[i] = (int)((x / shift) % 10);
        // do some work with it (print it here)
        for (int i = n - 1; i >= 0; i--) printf("%d", array[i]);
        putchar('\n');
    }
    delete[] array;
    return 0;
}

【讨论】:

  • 这和我的 pow(10, n-1) 和 pow(10, n) 解决方案一样----数字到数字数组;实际上,我更感兴趣的是找到一种直接生成数字的方法——给定“n”的一些类似排列的东西
【解决方案2】:

我想我可以为你写出整个事情,但这并不有趣。相反,我将仅概述基本方法,您可以通过填空自己完成答案。

考虑以这种方式表示的 n 位长数字:

 struct digit {
    struct digit *next;
    int n;    /* Digit 0-9 */
 };

以这种方式表示的单个数字可以这样打印出来:

void print_digit(struct digit *p)
{
    while (p)
    {
        std::cout << p->n;
        p=p->next;
    }
    std::cout << std::endl;
}

现在,让我们创建一个递归循环,它遍历所有可能的 n 位数字:

void iterate(int ndigits)
{
    for (int i=0; i<10; ++i)
    {
        if (ndigits > 1)
        {
            iterate(ndigits-1);
        }
        else
        {   // This is the last digit

            // Here be dragons
        }
    }
}

经过一番思考,您可以看到,例如,如果您调用iterate(4),那么当“hear be dragons”部分被执行时,这将位于一个四层深度的嵌套迭代堆栈中。将有四个深层次的for 循环,相互嵌套。并且,iterate(6) 将有六个,依此类推。

现在,考虑一下基于struct digit 的 n 位数字表示也是一个堆栈这一事实。

因此,这里的作业任务是使用这个递归迭代来动态构造这个链表,在堆栈上,“这里是龙”部分只需调用print_digit() 以便打印每个数字。

提示:iterate() 将需要更多参数,以便在初始调用 iterate() 时适当地使用这些参数,并为它们提供特定的预设值。

【讨论】:

    【解决方案3】:

    没有理由将每个数字的数字限制在0 - 9 的范围内。
    对于每个数字位置,我们将表示一个范围:

    std::pair<int,int> range;
    

    您示例中的每个循环都从范围的开头迭代到范围的结尾。
    所有的循环加在一起实际上只是一系列范围;每个嵌套循环负责生成数字的下一位。

    我们可以用以下方式表示:

    std::vector<std::pair<int, int>> ranges;
    

    如果您考虑嵌套 for loops 的工作原理,您可以使用两个指针在向量上模拟相同的功能。我已经做到了,并将功能包装到一个类中:

    //header
    class Range_Combinator {
    
    public:
    
      Range_Combinator(std::vector<std::pair<int, int>> const &ranges_in);
      std::vector<int> Next();
      std::vector<int> Current();
      bool Done();
    
    private:
    
      bool Adjust();
      void Reset_From_Current_Back(int from);
    
      std::vector<std::pair<int, int>> ranges;
      int current;
      int last;
      bool all_exausted;
      std::vector<int> current_vals;
    };
    

    //source
    Range_Combinator::Range_Combinator(
        std::vector<std::pair<int, int>> const &ranges_in) {
    
      ranges = ranges_in;
      last = ranges.size() - 1;
      current = last;
      all_exausted = false;
    
      for (auto it : ranges) {
        current_vals.push_back(it.first);
      }
    }
    
    std::vector<int> Range_Combinator::Next() {
      all_exausted = Adjust();
      return current_vals;
    }
    std::vector<int> Range_Combinator::Current() { return current_vals; }
    
    bool Range_Combinator::Done() { return all_exausted; }
    
    bool Range_Combinator::Adjust() {
      if (current_vals[current] < ranges[current].second) {
        current_vals[current]++;
      } else {
    
        while (current_vals[current] == ranges[current].second) {
          current--;
        }
    
        if (current < 0) {
          return true;
        }
    
        Reset_From_Current_Back(current + 1);
        current_vals[current]++;
        current = last;
      }
      return false;
    }
    
    void Range_Combinator::Reset_From_Current_Back(int from) {
    
      for (int i = from; i <= last; ++i) {
        current_vals[i] = ranges[i].first;
      }
    }
    

    这是你将如何使用它:

    //create range combinator
    std::vector<std::pair<int,int>> ranges{{1,2},{3,4}};
    Range_Combinator r(ranges);
    
    //print each number
    auto number = r.Current();
    while (!r.Done()){
        for (auto it: number) std::cout << it; std::cout << '\n';
        number = r.Next();
    }
    
    //prints: 13
    //        14
    //        23
    //        24
    

    【讨论】:

    • 我做了一个比较 1. 获取数字首先迭代范围“[ pow(10, N-1), pow(10, N) )” 然后将每个数字转换为数字数组; 2.这个解决方案。如果我只计算数字数组,时间成本是“7s vs 5s”;如果我计算数字数组和数字本身,时间成本是“7s vs 13s”;如果我使用您的解决方案(仅获取数字数组)并从 pow(10, N-1) 开始并行迭代,它将仅获取数字,将两者结合起来,时间成本为 5 秒。 (基于您的解决方案正在生成增加序列的事实,完全匹配迭代)。
    【解决方案4】:

    我不知道你为什么需要它,但你可以试试这个:

    size_t n = ; //whatever value
    
    unsigned char* x = new unsigned char[n]();
    x[0] = 1;   //make it n-digit 10000...000
    do
    {
        //process digits here
    
        ++x[n - 1];
        for (size_t i = n; i > 1; --i)
        {
            if (x[i - 1] == 10)
            {
                x[i - 1] = 0;
                ++x[i - 2];
            }
        }
    } while (x[0] < 10);
    
    delete [] x;
    

    您甚至可以处理非十进制数字,只需将硬编码的 10 替换为适当的数字即可。

    【讨论】:

    • 这是 5 岁儿童如何计数到 1000 的直接实现 :),很好。 +1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    相关资源
    最近更新 更多