【问题标题】:Generate all possible binary combinations with an arbitrary size array of uint8_t in C在 C 中使用任意大小的 uint8_t 数组生成所有可能的二进制组合
【发布时间】:2016-07-10 13:08:54
【问题描述】:

假设您在 C 中有一个 uint8_t 数组,数组的大小为 10。我想在数组中生成所有可能的二进制组合,这 80 位。

这可以通过 10 个嵌套循环来实现,每个循环对应数组的每个元素。但是,如果您不知道大小,则无法动态更改嵌套循环的数量。

是否有任何递归方法可以让您将这 80 位视为单个数字并从 000000000 ...... 直到 1111111111 ...... 其中 0 和 1 的数量等于 80?

我考虑过 GNU gmp,但最大的数字是 1024 位,您不能将它们作为代表实际数字的简单位来访问。

我只想做一个大计数器,从 0 到 2^80 计数,但在数组内部。

我想举个例子。包含两个字节的 uint8_t 数组:

|00000000|00000000|

|00000000|00000001|

|00000000|00000010| . . .

|00000000|11111111|

|00000001|00000000|

|00000001|00000001|

|00000001|00000010|

。 .

#include <gmp.h>
#include <stdio.h>
#include <assert.h>


int main()
{

  mpz_t n;

  int flag;

  //Initialize the number.

  mpz_init(n);

  mpz_set_ui(n,0);


 //Add one to the number */

  mpz_add_ui(n,n,1); /* n = n + 1 */

}

您将如何访问数字 n 并将内容复制到您的数组中?

memcpy(array, &n, 10) ?

【问题讨论】:

  • 我知道它可能永远不会结束。但如果数组的字节数较少,则它可以完成。
  • @2501 或者您需要简单地正确使用 GMP,其全部目的是允许有效处理此类操作。
  • @MarcusMüller OP 正在练习。使用 GMP 违背了这个意图。
  • @2501:我同意。这是学习如何使用进位的一个很好的课程。我确实认为 10 个字节有点太长了,因为它需要很长时间。我会先尝试 4 个字节。原理是一样的。
  • @J.East。你仍然需要展示你的尝试并提出一个特定的问题。我希望您不要期望有人会为您编写代码。

标签: c recursion


【解决方案1】:

您可以使用类似下面的代码。

但请记住,它的效率并不高。如果您想要更好的性能,请使用unsigned long longs 的数组。

替代解决方案是使用现有的库,如 GMP。

#include <stdio.h>
#include <stdint.h>

#define SZ 10
int main(void)
{
    uint8_t arr[SZ];
    for (int i = 0; i < SZ; i++)
        arr[i] = 0;

    for (int i = 0; i < SZ*2; i++)
        putchar('0');
    putchar('\n');

    while (1)
    {
        for (int i = 0; i <= SZ; i++)
        {
            if (i == SZ)
                goto stop;
            arr[i]++;
            if (arr[i] != 0xff)
                break;
        }

        const char *digits = "0123456789abcdef";
        for (int i = SZ-1; i >= 0; i--)
        {
            putchar(digits[(arr[i] >> 4) & 0xf]);
            putchar(digits[arr[i] & 0xf]);
        }
        putchar('\n');
    }
  stop:
    puts("Done! Wow, you're still alive?");
}

【讨论】:

  • 不要重新发明轮子。 GMP 可以做到这一点,但更好,而且 OP 已经知道 GMP。
  • @MarcusMüller 谢谢,我已将有关 GMP 的信息添加到答案中。虽然它正在重新发明轮子,但我确信 OP 这样做是为了实践,这没关系。
  • 谢谢,我认为这对以后的读者很有帮助! (尽管 OP 最初,查看编辑历史,声称这在 GMP 中是不可能的,这是一个误解,所以我不认为他在练习)
【解决方案2】:

你想做的事是不可能的,至少在这个宇宙中是不可能的。

所有 80 位组合与不超过 2⁸⁰ 的所有数字相同——这是一个非常大的数字。

正如您已经注意到的,这些值中的每一个都需要 10B 的内存 - 总计

10 B * 2⁸⁰ = 12089258196146291747061760 B
           = 10 B * 2⁵⁰Gi
           = 10 * 2⁴⁰ TiB

或者换句话说,这大约是 5.5 万亿个 SSD,每个容量为 2 TB。

假设每个 SSD 重约 80 克,那么这相当于 439,804,651 吨的质量。

因为您有点固执:即使您没有将其存储在 RAM 或磁盘上,也没有机会完成:这是您的“N 字节数组中的二进制组合”问题的图表。请注意,y 轴的单位为 10^28。

并且因为这不是真正有用的,对于对数 y 轴:

假设您的 PC 每秒可以通过十亿个这样的阵列,这就是它所花费的时间:

因此,对于一个 7 字节数组,仅生成数组并立即丢弃它们将需要两年多的时间。您的 10 B 阵列将需要大约 3800 万年的 CPU 时间。祝你好运!

无论如何,递归是最糟糕的算法选择,您可以只使用for 循环并计数。这里没有歧义,这将与 GMP 完美配合。我不知道你做错了什么。学习使用 GMP:

mpz_t number, limit;

mpz_init_set_ui(number, 0UL); //first value
mpz_init_set_ui(limit, 2UL); // limit=2
mpz_pow_ui (limit,limit,80); // limit=2**80

while(mpz_cmp(number,limit) < 0) { // while number smaller limit
     mpz_out_str(stdout, 2, number);
     mpz_add_ui(number, number, 1UL); //increase number
}

number 现在采用所有可能的 2^80 值,并以二进制形式打印。理论上。

【讨论】:

  • 我不想存储它们。我说的是一个 80 位的数组,你将位从 0000.... 翻转到 11111....... 除了这 80 位之外,没有什么可以存储的。
  • 是的,但真的吗?在你的有生之年,你不会看到这些几乎完成。甚至没有。
  • 我没有问你我是否固执。我没有问你它是否需要万亿年。我问怎么做。感谢您提供的精美图表,我可能有中国超级计算机可用...
  • @MarcusMüller:无法及时完成,但可以展示如何完成。使用较小的数组会更有意义,但原理完全相同。
  • @LukeVanIn OP:“我想在数组内生成所有可能的二进制组合,这些 80 位。” all 其中:/
【解决方案3】:

这是未经检查、未经优化的代码,如果运气好的话,可能会做你想做的事。

void add(uint8_t* in1,
        uint8_t* in2,
        uint8_t* out,
        size_t length)

{
  uint8_t carry = 0;
  for (size_t i = 0;
       i < length;
       ++i)
  {
    uint16_t res = in1[i] + in2[i] + carry;
    out[i] = (uint8_t)(res & 0xff);
    carry = res >> 8;
  }
}

【讨论】:

    【解决方案4】:

    一个快速而肮脏的 C 解决方案:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    
    typedef
    struct {
      size_t size;
      uint8_t data[];
    } bignum_t;
    
    bignum_t *bn_new(size_t size)
    {
      // assert size>0
      bignum_t *new=malloc(sizeof *new + sizeof(uint8_t[size]));
      if (new==NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
      }
      new->size=size;
      memset(new->data,0,size);
      return new;
    }
    
    void bn_free(bignum_t *bn)
    {
      free(bn);
    }
    
    bool bn_next(bignum_t *bn)
    {
      size_t i;
      for(i=0; i<bn->size; ++i)
        if (bn->data[i]==0xFF)
          bn->data[i]=0;
        else
          break;
    
      if (i==bn->size) return false;
      bn->data[i]++;
      return true;
    }
    
    void bn_print(bignum_t *bn)
    {
      putchar('[');
      for(size_t i=0; i<bn->size; ++i)
        printf(" %02X", bn->data[i]);
      printf(" ]");
    }
    
    int main(void)
    {
      bignum_t *bn=bn_new(10);
    
      do {
        bn_print(bn);
        putchar('\n');
      } while (bn_next(bn));
    
      bn_free(bn);
    
      return 0;
    }
    

    不要指望看到这个程序结束。

    【讨论】:

      【解决方案5】:

      这是非常可行的。您将需要:

      1. 用于存储字节的数组。
      2. 一种增加字节的方法。
      3. 一种在超出字节范围时进行溢出的方法。

      这一切都可以在一个函数中完成。在最坏的情况下,堆栈只需要递归到字节数(10 字节 = 10 次堆栈递归)。

      这是我之前准备的。它支持任意数量的字节,尽管它只使用三个字节让您有机会看到它运行到最后。

      #include <stdio.h>
      #include <math.h>
      #include <stdint.h>
      
      void increment(uint8_t * bytes, int size, int i) {
      
          if (i >= size) {
              // Number overflow, start at 0.
              return;
          }
      
          uint8_t b = bytes[i];
      
          // Check if byte will overflow.
          if (b == 0xFF) {
              // Byte overflow, carry overflow to next byte.
              b = 0;
              increment(bytes, size, i + 1);
          }
          else {
              // Increment byte.
              b ++;
          }
      
          bytes[i] = b;
      }
      
      int equal(const uint8_t * a, const uint8_t * b, int size) {
      
          for (int i = 0; i < size; i++) {
              if (a[i] != b[i]) {
                  // At least one byte is different.
                  return 0;
              }
          }
      
          // All bytes are the same.
          return 1;
      }
      
      void printBytes(const uint8_t * bytes, int size) {
      
          for (int i = 0; i < size; i++) {
              printf("%02x ", bytes[size - i - 1]);
          }
      
          printf("\n");
      }
      
      int main(int argc, const char * argv[]) {
      
          // Add more bytes here: { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
          uint8_t bytes[] = { 0, 0, 0 };
          uint8_t max[] = { 0xFF, 0xFF, 0xFF };
          int size = sizeof(bytes);
      
          while (!equal(bytes, max, size)) {
      
              printBytes(bytes, size);
      
              increment(bytes, size, 0);
          }
      
          return 0;
      }
      

      【讨论】:

      • 我认为 &bytes 是不需要的。数组的名字已经是一个地址,所以你需要没有&的简单字节。
      • 噢!这样的菜鸟错误!很好发现。
      • 为什么它从 00000000 00000000 0000007f 变为 00000000 00000000 ffffff80 ?
      • 呃……我要休息一天。检查溢出的 if 语句中有一个错误。现在修好了。还修复了打印语句 - 它为每个字节打印出完整的 32 位 int。因为它是十六进制的,所以每个字节只需要两个字符。
      • 不错!我对此感到抱歉。最后一个添加是#include 。现在工作正常!
      【解决方案6】:

      这是一个简单的 c 解决方案,打印每个数字的二进制等价物。

      虽然已经提出了一些解决方案,但我的解决方案是打印二进制并且是递归的。

      #include <stdio.h>
      #include <stdint.h>
      
      void Print(uint8_t *, int);
      void IncrementLoop(uint8_t *arr, int sz);
      int GetBit(uint8_t *arr, int idx);
      
      int main()
      {
          uint8_t ca[2] = {0xFF, 0};
      
          IncrementLoop((uint8_t *)ca, 16);
      
          return 0;
      }
      
      
      void IncrementLoop(uint8_t *arr, int sz)
      {
          int number_of_bytes = sz / 8;
          int count = 0;
      
          while((*arr) == (uint8_t)0xFF)
          {
              ++arr;
              ++count;
              if(count >= number_of_bytes)
              {
                  return;
              }
          }
      
          (*arr) += 1;
          for(;count;--count)
          {
              --arr;
              (*arr) &= 0;
          }
          Print(arr, sz);
          IncrementLoop(arr, sz);
      }
      
      void Print(uint8_t *arr, int sz)
      {
      
          while(sz)
          {
              printf("%d", GetBit(arr,sz-1));
              --sz;
          }
      
          printf("\n");
      }
      
      int GetBit(uint8_t *arr, int idx)
      {
          return (1 & (arr[(idx/8)] >> (idx % 8)));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-16
        • 2014-12-23
        • 2022-01-18
        • 2021-04-28
        • 1970-01-01
        相关资源
        最近更新 更多