找到将零插入位模式的所有方法

发布于 2024-09-03 14:37:52 字数 488 浏览 7 评论 0原文

由于某种原因,我一直在努力解决这个问题。我有 15 位代表一个数字。这些位必须与模式匹配。该模式是按照位开始的方式定义的:它们是该模式最右对齐的表示形式。假设模式为 1 4 1。位将为:

000000010111101

因此,一般规则是,获取模式中的每个数字,创建那么多位(在本例中为 1、4 或 1),然后至少有一个空格分隔他们。因此,如果它是 1 2 6 1 (它将是随机的):

001011011111101

从右对齐版本开始,我想生成满足该模式的每个可能的数字。位的数量将存储在变量中。因此,对于一个简单的情况,假设它是 5 位,初始位模式为:00101。我想生成:

00101 01001 01010 10001 10010 10100

我正在尝试在 Objective-C 中执行此操作,但任何类似于 C 的东西都可以。我似乎无法为此想出一个好的递归算法。在上面的例子中这是有道理的,但是当我开始使用 12431 并必须跟踪所有内容时,它就崩溃了。

I've been struggling to wrap my head around this for some reason. I have 15 bits that represent a number. The bits must match a pattern. The pattern is defined in the way the bits start out: they are in the most flush-right representation of that pattern. So say the pattern is 1 4 1. The bits will be:

000000010111101

So the general rule is, take each number in the pattern, create that many bits (1, 4 or 1 in this case) and then have at least one space separating them. So if it's 1 2 6 1 (it will be random):

001011011111101

Starting with the flush-right version, I want to generate every single possible number that meets that pattern. The # of bits will be stored in a variable. So for a simple case, assume it's 5 bits and the initial bit pattern is: 00101. I want to generate:

00101
01001
01010
10001
10010
10100

I'm trying to do this in Objective-C, but anything resembling C would be fine. I just can't seem to come up with a good recursive algorithm for this. It makes sense in the above example, but when I start getting into 12431 and having to keep track of everything it breaks down.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

铁轨上的流浪者 2024-09-10 14:37:52

希望这会让您更容易理解它(请用手中的笔和纸阅读本文)。

假设零的数量(从右侧开始)为 x1、x2、...、xn。例如:如果位模式为 00001110001001,则 x1 = 0、x2 = 2、x3 = 3、x4 = 4。n 比 1 的块数多 1。请注意,知道 x1、x2、...、xn 就足以找出位模式。

现在,如果 1 的总数为 S,可用位数为 M,那么我们必须有

x1 + x2 + ... + xn = M - S

且 x1 ≥ 0,xn ≥ 0,x2 ≥ 1, x3 ≥ 1, ...

设 z1 = x1 + 1
和 zn = xn + 1

因此我们有

z1 + x2 + ... x< sub>n-1 + zn = M - S + 2

其中 z1 ≥ 1,x2 ≥ 1, x3 ≥ 1, ..., zn ≥ 1。

现在考虑 M-S+2 个项目的分区,其中每个分区至少有一个项目。任何分区都对应于上式的一个解,并且解对应于 1-1 方式的分区。

将 M-S+2 个物品沿一条线放置。要获得分区,请考虑将 n-1 根棍子放置在物品之间的 M-S+2-1 = M-S+1 可用位置中。

因此,解决方案(以及最终您所需的位模式)唯一对应于在 M-S+1 个点中选择 n-1 个点的方式。

在 5 位的情况下,1 位为 1 和 1。

您有 n = 3、M = 5 和 S = 2。

因此您有 M-S+1 选择 n-1 = 4 选择 2 = 6 种可能性。

枚举 n 选择 r 组合是一个标准问题,您应该在网络上找到各种各样的解决方案(其中一些非常聪明!)。

有关示例,请参见此处: http://compprog.files.wordpress.com/ 2007/10/comb1.c 似乎支持“惰性”枚举:next_combination 并且不需要大量内存。

Hopefully, this will make it easier to wrap your head around it (please read through this with a pen and paper in hand).

Say the number of zeroes (starting from the right) is x1, x2, ..., xn. eg: if the bit pattern is 00001110001001 then x1 = 0, x2 = 2, x3 = 3, x4 = 4. n is one more than the number of blocks of ones. Observe that knowing x1, x2, ..., xn is enough to figure out the bit-pattern.

Now if the total number of 1's you have is S and total number of bits you have available is M then we must have that

x1 + x2 + ... + xn = M - S

and x1 ≥ 0, xn ≥ 0, x2 ≥ 1, x3 ≥ 1, ...

Let z1 = x1 + 1
and zn = xn + 1

Thus we have

z1 + x2 + ... xn-1 + zn = M - S + 2

Where z1 ≥ 1, x2 ≥ 1, x3 ≥ 1, ..., zn ≥ 1.

Now consider a partition of M-S+2 items where each partition has at least one item. Any partition corresponds to a solution of the above equation and a solution corresponds to a partition in a 1-1 fashion.

Place the M-S+2 items along a line. To get a partition, consider placing n-1 sticks in the M-S+2-1 = M-S+1 spots available, between the items.

Thus a solution (and ultimately your required bit-pattern) uniquely corresponds to a way of choosing n-1 spots among M-S+1 spots.

In case of 5 bits, and 1 bits being 1 and 1.

You have n = 3, M = 5, and S = 2.

Thus you have M-S+1 choose n-1 = 4 choose 2 = 6 possiblities.

Enumerating n choose r combinations is a standard problem and you should find a large variety of solutions (some of them very clever!) for that on the web.

For an example see here: http://compprog.files.wordpress.com/2007/10/comb1.c which seems to support a 'lazy' enumeration: next_combination and does not require huge amounts of memory.

情仇皆在手 2024-09-10 14:37:52

我不会向您提供 Objective-C 代码,主要是因为:

  • 我对 Objective-C 的了解非常肤浅。
  • 我不想用 C 这样的语言编写所需的所有内存管理代码,而且无论如何它只会降低可读性。

相反,我会给你一些想法和一些代码,展示如何使用生成器和垃圾收集(在本例中为 Python)以更高级的语言实现这一点,并提示如何在没有生成器的情况下实现这一点。如果您自己做不到,希望其他人能够为您移植代码。

我会以稍微不同的方式思考你的问题:

  • 在你最初的“右对齐”模式中有多少个前导零。
  • 有多少种方法可以将该数量的零划分为 n 个分区。

在上一个示例中,您有两个前导零和三个带有分隔符“10”和“1”的分区:

2 0 0: 00101  
1 1 0: 01001   
1 0 1: 01010   
0 2 0: 10001   
0 1 1: 10010   
0 0 2: 10100

分隔符始终采用 111..10 形式,最后一个分隔符仅为 111 ..1 没有尾随零。

要枚举上述分区,请使用 Python 中如下所示的函数:

def partitions(n, x):
    if n == 1:
        yield [x]
    else:
        for i in range(x + 1):
            for p in partitions(n - 1, x - i):
                yield [i] + p

for p in partitions(3, 2):
    print p

结果:

[0, 0, 2]
[0, 1, 1]
[0, 2, 0]
[1, 0, 1]
[1, 1, 0]
[2, 0, 0]

一旦有了这些分区,构建模式就很简单。

一项挑战是 Objective-C 没有对 Yield 构造的内置支持。对上述函数进行以下重写可能会更容易转换为 Objective-C:

def partitions(n, x):
    if n == 1:
        return [[x]]
    else:
        result = []
        for i in range(x + 1):
            for p in partitions(n - 1, x - i):
                result.append([i] + p)
        return result

我希望这对您有用。

I'm not going to give you Objective-C code mainly because:

  • I only know Objective-C on a very superficial level.
  • I don't have the desire to write all the memory management code required to get this working in a language like C, and it would only detract from the readability anyway.

Instead I will give you some ideas and some code showing how I would implement this in a higher language with generators and garbage collection (Python in this case) and a hint on how to do it without generators. Hopefully someone else may be able to port the code for you if you cannot do it yourself.

I would think about your problem in a slightly different way:

  • How many leading zeros are there in your initial "flushed-right" pattern.
  • How many ways are there to partition that number of zeros into n partitions.

In your last example you have two leading zeros and three partitions with separators '10' and '1':

2 0 0: 00101  
1 1 0: 01001   
1 0 1: 01010   
0 2 0: 10001   
0 1 1: 10010   
0 0 2: 10100

The separators are always of the form 111..10 except the last which is just 111..1 without the trailing zero.

To enumerate the above partitions use a function like the following in Python:

def partitions(n, x):
    if n == 1:
        yield [x]
    else:
        for i in range(x + 1):
            for p in partitions(n - 1, x - i):
                yield [i] + p

for p in partitions(3, 2):
    print p

Result:

[0, 0, 2]
[0, 1, 1]
[0, 2, 0]
[1, 0, 1]
[1, 1, 0]
[2, 0, 0]

Once you have these partitions it is simple to construct the patterns.

One challenge is that Objective-C doesn't have built-in support for the yield construct. The following rewrite of the above function may be easier to convert to Objective-C:

def partitions(n, x):
    if n == 1:
        return [[x]]
    else:
        result = []
        for i in range(x + 1):
            for p in partitions(n - 1, x - i):
                result.append([i] + p)
        return result

I hope that is of some use to you.

始终不够爱げ你 2024-09-10 14:37:52

基于 @Mark Byers 的白痴的答案你的任务可以重新表述如下:枚举

K个零放入N个位置的所有方法(参见重复组合星星和酒吧)。

示例:对于 15 位和 1 2 6 1 模式,有 N=5 个位置(数字之前/之后以及 1 之间)放置 K=2 个零(同花顺的前导零的数量)正确的数字)。方式数是二项式(N + K - 1, K) 即二项式(5+ 2-1, 2) = 15.

下面代码中的关键函数是 next_combination_counts()comb2number()

C 输出中的完整程序

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

#define SIZE(arr) (sizeof(arr)/sizeof(*(arr)))

#define PRInumber "u"
typedef unsigned number_t;

// swap values pointed to by the pointer
static void
iter_swap(int* ia, int* ib) {
  int t = *ia;
  *ia = *ib;
  *ib = t;
}

// see boost::next_combinations_counts()
// http://photon.poly.edu/~hbr/boost/combinations.html
// http://photon.poly.edu/~hbr/boost/combination.hpp
static bool 
next_combination_counts(int* first, int* last) {
  /*
0 0 2 
0 1 1 
0 2 0 
1 0 1 
1 1 0 
2 0 0 
   */
    int* current = last;
    while (current != first && *(--current) == 0) {
    }
    if (current == first) {
        if (first != last && *first != 0)
            iter_swap(--last, first);
        return false;
    }
    --(*current);
    iter_swap(--last, current);
    ++(*(--current));
    return true;
}

// convert combination and pattern to corresponding number
// example: comb=[2, 0, 0] pattern=[1,1] => num=5 (101 binary)
static number_t 
comb2number(int comb[], int comb_size, int pattern[], int pattern_size) {
  if (pattern_size == 0)
    return 0;
  assert(pattern_size > 0);
  assert(comb_size > pattern_size);

  // 111 -> 1000 - 1 -> 2**3 - 1 -> (1 << 3) - 1
  // 111 << 2 -> 11100
  number_t num = ((1 << pattern[pattern_size-1]) - 1) << comb[pattern_size];  
  int len = pattern[pattern_size-1] + comb[pattern_size];
  for (int i = pattern_size - 1; i--> 0; ) {
    num += ((1 << pattern[i]) - 1) << (comb[i+1] + 1 + len);
    len += pattern[i] + comb[i+1] + 1;
  }  

  return num;
}

// print binary representation of number
static void 
print_binary(number_t number) {
  if (number > 0) {
    print_binary(number >> 1);
    printf("%d", number & 1);
  }
}

// print array
static void
printa(int arr[], int size, const char* suffix) {
  printf("%s", "{");
  for (int i = 0; i < (size - 1); ++i)
    printf("%d, ", arr[i]);
  if (size > 0)
    printf("%d", arr[size - 1]);
  printf("}%s", suffix);
}

static void 
fill0(int* first, int* last) {
  for ( ; first != last; ++first)
    *first = 0;
}

// generate {0,0,...,0,nzeros} combination
static void
init_comb(int comb[], int comb_size, int nzeros) {
  fill0(comb, comb + comb_size);
  comb[comb_size-1] = nzeros;
}

static int
sum(int* first, int* last) {
  int s = 0;
  for ( ; first != last; ++first)
    s += *first;
  return s;
}

// calculated max width required to print number (in PRInumber format)
static int 
maxwidth(int comb[], int comb_size, int pattern[], int pattern_size) {
  int initial_comb[comb_size];

  int nzeros = sum(comb, comb + comb_size);
  init_comb(initial_comb, comb_size, nzeros);
  return snprintf(NULL, 0, "%" PRInumber, 
                  comb2number(initial_comb, comb_size, pattern, pattern_size)); 
}

static void 
process(int comb[], int comb_size, int pattern[], int pattern_size) {
  // print combination and pattern
  printa(comb, comb_size, " ");
  printa(pattern, pattern_size, " ");
  // print corresponding number
  for (int i = 0; i < comb[0]; ++i)
    printf("%s", "0");
  number_t number = comb2number(comb, comb_size, pattern, pattern_size);
  print_binary(number);
  const int width = maxwidth(comb, comb_size, pattern, pattern_size);
  printf(" %*" PRInumber "\n", width, number);
}

// reverse the array
static void 
reverse(int a[], int n) {
  for (int i = 0, j = n - 1; i < j; ++i, --j) 
    iter_swap(a + i, a + j);  
}

// convert number to pattern
// 101101111110100 -> 1, 2, 6, 1
static int 
number2pattern(number_t num, int pattern[], int nbits, int comb[]) {
  // SIZE(pattern) >= nbits
  // SIZE(comb) >= nbits + 1
  fill0(pattern, pattern + nbits);
  fill0(comb, comb + nbits + 1);

  int i = 0;
  int pos = 0;
  for (; i < nbits && num; ++i) {
    // skip trailing zeros
    for ( ; num && !(num & 1); num >>= 1, ++pos)
      ++comb[i];
    // count number of 1s
    for ( ; num & 1; num >>=1, ++pos) 
      ++pattern[i];
  }
  assert(i == nbits || pattern[i] == 0);  
  const int pattern_size = i;  

  // skip comb[0]
  for (int j = 1; j < pattern_size; ++j) --comb[j];
  comb[pattern_size] = nbits - pos;

  reverse(pattern, pattern_size);
  reverse(comb, pattern_size+1);
  return pattern_size;
}

int 
main(void) {
  number_t num = 11769; 
  const int nbits = 15;

  // clear hi bits (required for `comb2number() != num` relation)
  if (nbits < 8*sizeof(number_t))
    num &=  ((number_t)1 << nbits) - 1;
  else
    assert(nbits == 8*sizeof(number_t));

  // `pattern` defines how 1s are distributed in the number
  int pattern[nbits];
  // `comb` defines how zeros are distributed 
  int comb[nbits+1];
  const int pattern_size = number2pattern(num, pattern, nbits, comb);
  const int comb_size = pattern_size + 1;

  // check consistency
  // . find number of leading zeros in a flush-right version
  int nzeros = nbits;
  for (int i = 0; i < (pattern_size - 1); ++i)
    nzeros -= pattern[i] + 1;
  assert(pattern_size > 0);
  nzeros -= pattern[pattern_size - 1];
  assert(nzeros>=0);

  // . the same but using combination
  int nzeros_comb = sum(comb, comb + comb_size);
  assert(nzeros_comb == nzeros);

  // enumerate all combinations 
  printf("Combination Pattern Binary Decimal\n");
  assert(comb2number(comb, comb_size, pattern, pattern_size) == num);
  process(comb, comb_size, pattern, pattern_size); // process `num`

  // . until flush-left number 
  while(next_combination_counts(comb, comb + comb_size))
    process(comb, comb_size, pattern, pattern_size);

  // . until `num` number is encounterd  
  while (comb2number(comb, comb_size, pattern, pattern_size) != num) {
    process(comb, comb_size, pattern, pattern_size);
    (void)next_combination_counts(comb, comb + comb_size);
  }

  return 0;
}

Combination Pattern Binary Decimal
{1, 0, 0, 1, 0} {1, 2, 6, 1} 010110111111001 11769
{1, 0, 1, 0, 0} {1, 2, 6, 1} 010110011111101 11517
{1, 1, 0, 0, 0} {1, 2, 6, 1} 010011011111101  9981
{2, 0, 0, 0, 0} {1, 2, 6, 1} 001011011111101  5885
{0, 0, 0, 0, 2} {1, 2, 6, 1} 101101111110100 23540
{0, 0, 0, 1, 1} {1, 2, 6, 1} 101101111110010 23538
{0, 0, 0, 2, 0} {1, 2, 6, 1} 101101111110001 23537
{0, 0, 1, 0, 1} {1, 2, 6, 1} 101100111111010 23034
{0, 0, 1, 1, 0} {1, 2, 6, 1} 101100111111001 23033
{0, 0, 2, 0, 0} {1, 2, 6, 1} 101100011111101 22781
{0, 1, 0, 0, 1} {1, 2, 6, 1} 100110111111010 19962
{0, 1, 0, 1, 0} {1, 2, 6, 1} 100110111111001 19961
{0, 1, 1, 0, 0} {1, 2, 6, 1} 100110011111101 19709
{0, 2, 0, 0, 0} {1, 2, 6, 1} 100011011111101 18173
{1, 0, 0, 0, 1} {1, 2, 6, 1} 010110111111010 11770

Building on @Mark Byers's and Moron's answers your task can be reformulated as follows:

Enumerate all ways to put K zeros into N places (see combinations with repetition and Stars and bars).

Example: For 15 bits and 1 2 6 1 pattern there are N=5 places (before/after the number and between 1s) to put K=2 zeros (number of leading zeros for a flush-right number). Number of ways is binomial(N + K - 1, K) i.e., binomial(5+2-1, 2) = 15.

The key functions in the code below are next_combination_counts() and comb2number().

Full program in C

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

#define SIZE(arr) (sizeof(arr)/sizeof(*(arr)))

#define PRInumber "u"
typedef unsigned number_t;

// swap values pointed to by the pointer
static void
iter_swap(int* ia, int* ib) {
  int t = *ia;
  *ia = *ib;
  *ib = t;
}

// see boost::next_combinations_counts()
// http://photon.poly.edu/~hbr/boost/combinations.html
// http://photon.poly.edu/~hbr/boost/combination.hpp
static bool 
next_combination_counts(int* first, int* last) {
  /*
0 0 2 
0 1 1 
0 2 0 
1 0 1 
1 1 0 
2 0 0 
   */
    int* current = last;
    while (current != first && *(--current) == 0) {
    }
    if (current == first) {
        if (first != last && *first != 0)
            iter_swap(--last, first);
        return false;
    }
    --(*current);
    iter_swap(--last, current);
    ++(*(--current));
    return true;
}

// convert combination and pattern to corresponding number
// example: comb=[2, 0, 0] pattern=[1,1] => num=5 (101 binary)
static number_t 
comb2number(int comb[], int comb_size, int pattern[], int pattern_size) {
  if (pattern_size == 0)
    return 0;
  assert(pattern_size > 0);
  assert(comb_size > pattern_size);

  // 111 -> 1000 - 1 -> 2**3 - 1 -> (1 << 3) - 1
  // 111 << 2 -> 11100
  number_t num = ((1 << pattern[pattern_size-1]) - 1) << comb[pattern_size];  
  int len = pattern[pattern_size-1] + comb[pattern_size];
  for (int i = pattern_size - 1; i--> 0; ) {
    num += ((1 << pattern[i]) - 1) << (comb[i+1] + 1 + len);
    len += pattern[i] + comb[i+1] + 1;
  }  

  return num;
}

// print binary representation of number
static void 
print_binary(number_t number) {
  if (number > 0) {
    print_binary(number >> 1);
    printf("%d", number & 1);
  }
}

// print array
static void
printa(int arr[], int size, const char* suffix) {
  printf("%s", "{");
  for (int i = 0; i < (size - 1); ++i)
    printf("%d, ", arr[i]);
  if (size > 0)
    printf("%d", arr[size - 1]);
  printf("}%s", suffix);
}

static void 
fill0(int* first, int* last) {
  for ( ; first != last; ++first)
    *first = 0;
}

// generate {0,0,...,0,nzeros} combination
static void
init_comb(int comb[], int comb_size, int nzeros) {
  fill0(comb, comb + comb_size);
  comb[comb_size-1] = nzeros;
}

static int
sum(int* first, int* last) {
  int s = 0;
  for ( ; first != last; ++first)
    s += *first;
  return s;
}

// calculated max width required to print number (in PRInumber format)
static int 
maxwidth(int comb[], int comb_size, int pattern[], int pattern_size) {
  int initial_comb[comb_size];

  int nzeros = sum(comb, comb + comb_size);
  init_comb(initial_comb, comb_size, nzeros);
  return snprintf(NULL, 0, "%" PRInumber, 
                  comb2number(initial_comb, comb_size, pattern, pattern_size)); 
}

static void 
process(int comb[], int comb_size, int pattern[], int pattern_size) {
  // print combination and pattern
  printa(comb, comb_size, " ");
  printa(pattern, pattern_size, " ");
  // print corresponding number
  for (int i = 0; i < comb[0]; ++i)
    printf("%s", "0");
  number_t number = comb2number(comb, comb_size, pattern, pattern_size);
  print_binary(number);
  const int width = maxwidth(comb, comb_size, pattern, pattern_size);
  printf(" %*" PRInumber "\n", width, number);
}

// reverse the array
static void 
reverse(int a[], int n) {
  for (int i = 0, j = n - 1; i < j; ++i, --j) 
    iter_swap(a + i, a + j);  
}

// convert number to pattern
// 101101111110100 -> 1, 2, 6, 1
static int 
number2pattern(number_t num, int pattern[], int nbits, int comb[]) {
  // SIZE(pattern) >= nbits
  // SIZE(comb) >= nbits + 1
  fill0(pattern, pattern + nbits);
  fill0(comb, comb + nbits + 1);

  int i = 0;
  int pos = 0;
  for (; i < nbits && num; ++i) {
    // skip trailing zeros
    for ( ; num && !(num & 1); num >>= 1, ++pos)
      ++comb[i];
    // count number of 1s
    for ( ; num & 1; num >>=1, ++pos) 
      ++pattern[i];
  }
  assert(i == nbits || pattern[i] == 0);  
  const int pattern_size = i;  

  // skip comb[0]
  for (int j = 1; j < pattern_size; ++j) --comb[j];
  comb[pattern_size] = nbits - pos;

  reverse(pattern, pattern_size);
  reverse(comb, pattern_size+1);
  return pattern_size;
}

int 
main(void) {
  number_t num = 11769; 
  const int nbits = 15;

  // clear hi bits (required for `comb2number() != num` relation)
  if (nbits < 8*sizeof(number_t))
    num &=  ((number_t)1 << nbits) - 1;
  else
    assert(nbits == 8*sizeof(number_t));

  // `pattern` defines how 1s are distributed in the number
  int pattern[nbits];
  // `comb` defines how zeros are distributed 
  int comb[nbits+1];
  const int pattern_size = number2pattern(num, pattern, nbits, comb);
  const int comb_size = pattern_size + 1;

  // check consistency
  // . find number of leading zeros in a flush-right version
  int nzeros = nbits;
  for (int i = 0; i < (pattern_size - 1); ++i)
    nzeros -= pattern[i] + 1;
  assert(pattern_size > 0);
  nzeros -= pattern[pattern_size - 1];
  assert(nzeros>=0);

  // . the same but using combination
  int nzeros_comb = sum(comb, comb + comb_size);
  assert(nzeros_comb == nzeros);

  // enumerate all combinations 
  printf("Combination Pattern Binary Decimal\n");
  assert(comb2number(comb, comb_size, pattern, pattern_size) == num);
  process(comb, comb_size, pattern, pattern_size); // process `num`

  // . until flush-left number 
  while(next_combination_counts(comb, comb + comb_size))
    process(comb, comb_size, pattern, pattern_size);

  // . until `num` number is encounterd  
  while (comb2number(comb, comb_size, pattern, pattern_size) != num) {
    process(comb, comb_size, pattern, pattern_size);
    (void)next_combination_counts(comb, comb + comb_size);
  }

  return 0;
}

Output:

Combination Pattern Binary Decimal
{1, 0, 0, 1, 0} {1, 2, 6, 1} 010110111111001 11769
{1, 0, 1, 0, 0} {1, 2, 6, 1} 010110011111101 11517
{1, 1, 0, 0, 0} {1, 2, 6, 1} 010011011111101  9981
{2, 0, 0, 0, 0} {1, 2, 6, 1} 001011011111101  5885
{0, 0, 0, 0, 2} {1, 2, 6, 1} 101101111110100 23540
{0, 0, 0, 1, 1} {1, 2, 6, 1} 101101111110010 23538
{0, 0, 0, 2, 0} {1, 2, 6, 1} 101101111110001 23537
{0, 0, 1, 0, 1} {1, 2, 6, 1} 101100111111010 23034
{0, 0, 1, 1, 0} {1, 2, 6, 1} 101100111111001 23033
{0, 0, 2, 0, 0} {1, 2, 6, 1} 101100011111101 22781
{0, 1, 0, 0, 1} {1, 2, 6, 1} 100110111111010 19962
{0, 1, 0, 1, 0} {1, 2, 6, 1} 100110111111001 19961
{0, 1, 1, 0, 0} {1, 2, 6, 1} 100110011111101 19709
{0, 2, 0, 0, 0} {1, 2, 6, 1} 100011011111101 18173
{1, 0, 0, 0, 1} {1, 2, 6, 1} 010110111111010 11770
っ左 2024-09-10 14:37:52

假设我们:

000101101

首先,计算 0 (5),并计算 1 包围的 0 组 (1)。我们现在可以忘记 1。任何组合中每组零的数量可以是一个描述为的列表(其中 + 表示“或更多”):

[0+, 1+, 1+, 0+] where the sum is 6

这只是类似问题的变体:找到 N 个非负的所有集合总和为 K 的整数。例如:

[0+, 0+, 0+, 0+] where the sum is 6

现在,要解决此问题,请从 N=1 开始。解决方案显然就是[6]。当 N=2 时,解决方案是:

[0,6] [1,5] [2,4] [3,3] [4,2] [5,1] [6,0]

重要的是要注意这里的潜在主题:随着左侧变得更富有,右侧变得更贫穷。我将使用 Haskell 来描述该算法,因为事实证明它对于此类事情非常优雅:

sumsTo k n
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [0..k]
        s <- sumsTo (k-i) (n-1)
        return (i : s)

n == 1 情况非常容易理解:它只返回一个组合:<代码>[k]。在n>中1 情况,这里实际上发生了一个嵌套循环。它基本上是在说:

for each number i from 0 to k
    for each s in sumsTo (k-i) (n-1)
        prepend i to s

虽然这个算法并不能完全解决你的问题,但总体上了解一下还是有好处的。

现在,我们希望算法以不同的方式运行,以处理中间列表项不能为零的情况。对于这些情况,我们希望使用 i <- [1..k] 而不是 i <- [0..k]。结束数字并不重要,因为它没有自由意志(它仅取决于前面项目的总和)。更接近的是,我们可能会说:

sumsTo k n
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [1..k]
        s <- sumsTo (k-i) (n-1)
        return (i : s)

但是,我们希望我们的第一个项目能够从零开始。为了修补这个问题,我们可以说:

sumsTo k n first_start
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [first_start..k]
        s <- sumsTo (k-i) (n-1) 1
             -- make subsequent sumsTo calls start from 1 instead of 0
        return (i : s)

给定 K(0 的数量)和 N(内部 0 组的数量加上 2),这为我们提供了所需的序列)。剩下的就是对序列进行字符串化(例如将 [1,1,0] 转换为“01”)。我们甚至可以将字符串化直接嵌入到我们的递归算法中。

总而言之,这是 Haskell 中的一个解决方案:

import Data.List

sumsTo k n first_start
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [first_start..k]
        s <- sumsTo (k-i) (n-1) 1
        return (i : s)

-- remove any xes found at the beginning or end of list
trim x list
    = dropWhile (== x)
    $ reverse
    $ dropWhile (== x)
    $ reverse
    $ list

-- interleave [1,3,5] [2,4] = [1,2,3,4,5]
interleave xs ys = concat $ transpose [xs,ys]

solve input = solutions where
    -- pull out groups of 1s and put them in a list
    groupsOfOnes = filter ((== '1') . head) $ group input

    -- count 0s
    k = length $ filter (== '0') input

    -- trim outer 0s
    trimmed = trim '0' input

    -- count inner groups of 0s
    innerGroups = length $ filter ((== '0') . head) $ group trimmed

    -- n is number of outer groups (which is innerGroups + 2)
    n = innerGroups + 2

    -- compute our solution sequences
    -- reverse them so our answer will be in lexicographic order
    sequences = reverse $ sumsTo k n 0

    -- to transform a sequence into a group of zeros,
    -- simply make strings of the indicated number of zeros
    groupsOfZeros seq = [replicate n '0' | n <- seq]

    -- a solution for a sequence is just the zeros interleaved with the ones
    solution seq = concat $ interleave (groupsOfZeros seq) groupsOfOnes

    solutions = map solution sequences

main = do
    input <- getLine
    putStr $ unlines $ solve input

总之,我建议学习 Haskell,以便您可以更快地构建算法原型:-)

Suppose we have:

000101101

First, count the 0s (5), and count the groups of 0s surrounded by 1s (1). We can forget about the 1s for now. The number of zeros per group in any combination can be a list described as (where + means "or more"):

[0+, 1+, 1+, 0+] where the sum is 6

This is just a variation of a similar problem: find all sets of N non-negative integers that sum to K. For example:

[0+, 0+, 0+, 0+] where the sum is 6

Now, to solve this, start with N=1. The solution is obviously just [6]. With N=2, the solution is:

[0,6] [1,5] [2,4] [3,3] [4,2] [5,1] [6,0]

It's important to notice an underlying theme here: as the left side gets richer, the right side gets poorer. I'll use Haskell to describe the algorithm, as it turns out to be quite elegant for this type of thing:

sumsTo k n
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [0..k]
        s <- sumsTo (k-i) (n-1)
        return (i : s)

The n == 1 case is pretty easy to understand: it just returns one combination: [k]. In the n > 1 case, there's actually a nested loop going on here. It's basically saying:

for each number i from 0 to k
    for each s in sumsTo (k-i) (n-1)
        prepend i to s

Although this algorithm doesn't quite solve your problem, it's good to know in general.

Now, we want the algorithm to operate differently to handle how the middle list items cannot be zero. For those cases, we want to use i <- [1..k] instead of i <- [0..k]. It doesn't matter for the end number since it has no free will (it depends solely on the sum of the previous items). Getting closer, we might say:

sumsTo k n
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [1..k]
        s <- sumsTo (k-i) (n-1)
        return (i : s)

However, we want our first item to be able to start at zero. To patch that up, we can say:

sumsTo k n first_start
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [first_start..k]
        s <- sumsTo (k-i) (n-1) 1
             -- make subsequent sumsTo calls start from 1 instead of 0
        return (i : s)

This gives us the sequences we need given K (the number of 0s) and N (the number of inner groups of 0s plus 2). All that remains is stringifying the sequences (e.g. turning [1,1,0] into "01"). We could go as far as embedding the stringification directly into our recursive algorithm.

Summing it all up, here is a solution in Haskell:

import Data.List

sumsTo k n first_start
    | n == 1 = [[k]]
    | n > 1  = do
        i <- [first_start..k]
        s <- sumsTo (k-i) (n-1) 1
        return (i : s)

-- remove any xes found at the beginning or end of list
trim x list
    = dropWhile (== x)
    $ reverse
    $ dropWhile (== x)
    $ reverse
    $ list

-- interleave [1,3,5] [2,4] = [1,2,3,4,5]
interleave xs ys = concat $ transpose [xs,ys]

solve input = solutions where
    -- pull out groups of 1s and put them in a list
    groupsOfOnes = filter ((== '1') . head) $ group input

    -- count 0s
    k = length $ filter (== '0') input

    -- trim outer 0s
    trimmed = trim '0' input

    -- count inner groups of 0s
    innerGroups = length $ filter ((== '0') . head) $ group trimmed

    -- n is number of outer groups (which is innerGroups + 2)
    n = innerGroups + 2

    -- compute our solution sequences
    -- reverse them so our answer will be in lexicographic order
    sequences = reverse $ sumsTo k n 0

    -- to transform a sequence into a group of zeros,
    -- simply make strings of the indicated number of zeros
    groupsOfZeros seq = [replicate n '0' | n <- seq]

    -- a solution for a sequence is just the zeros interleaved with the ones
    solution seq = concat $ interleave (groupsOfZeros seq) groupsOfOnes

    solutions = map solution sequences

main = do
    input <- getLine
    putStr $ unlines $ solve input

In conclusion, I recommend learning Haskell so you can prototype algorithms more quickly :-)

末骤雨初歇 2024-09-10 14:37:52

这是理论问题还是实践问题?您是否需要最优的 O(N) 或相当好的执行时间?如果这是一个实际问题,并且除非它在某些内容的内部循环中,否则只需检查每个 15 位数字就应该足够快了。只有 32k 个数字。

只需获取您的数字的分区,如下所示:

void get_groups(ushort x, int* groups) {
  int change_group = 0;
  while(x) {
    if (x & 1) {
      ++(*groups);
      change_group = 1;
    } else {
      if (change_group) {
        ++groups;
        change_group = 0;
      }
    }
    x >>= 1;
  }
}

然后,对于每个 15 位数字,检查它是否生成与您的初始数字相同的组数组。

注意:groups 数组应该能够容纳最大数量的组(例如,15 位数字的大小应该为 8)。

Is this a theoretical or practical problem? Do you need the optimal O(N) or reasonably good execution time? If this is a practical problem, and unless it's in the inner loop of something, just checking every 15-bit number should be fast enough. It's only 32k numbers.

Just get the partitions for your number, something like this:

void get_groups(ushort x, int* groups) {
  int change_group = 0;
  while(x) {
    if (x & 1) {
      ++(*groups);
      change_group = 1;
    } else {
      if (change_group) {
        ++groups;
        change_group = 0;
      }
    }
    x >>= 1;
  }
}

And then, for every 15-bit number, check if it produces the same groups array as your initial number.

Note: The groups array should be able to accommodate the maximum number of groups (e.g. should have size 8 for 15-bit numbers).

甜`诱少女 2024-09-10 14:37:52

如果您的模式是 00101(如示例所示),那么您可以考虑生成六个模式的一种方法是:

查看模式 0000,然后选择两个零来生成更改为 。现在您将得到类似于 0011 的内容。现在只需在每个 1 之后插入一个 0(最后一个除外)。现在您将拥有 00101

请注意,您正在从四个位置中选择两个,并且有六种不同的可能方法可以做到这一点(与您所写的一致)。现在您只需要一种方法来选择要翻转的两位。您可以使用类似于此链接中指定的算法:生成组合< /a>

If your pattern is 00101, as in the example, then one way you can consider for generating the six patterns is:

Look at the pattern 0000 then choose two of the zeroes to change to ones. Now you'll have something like 0011. Now just insert a 0 after each 1 (except for the last one). Now you'll have 00101.

Note that you are choosing two out of four places, and there are six different possible ways you can do that (consistent with what you wrote). Now you just need a way to choose the two bits to flip. You can use something like the algorithm specified at this link: Generating Combinations

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文