最优雅的方式扩展卡手套装

时间:2010-06-03 22:23:35

标签: algorithm language-agnostic

我正在存放4张牌,以不同的方式对待手牌,例如:

9h 8h 7c 6c

相同
9d 8d 7h 6h

因为你可以用另一套衣服替换一件衣服并且有相同的东西。使用通配符进行套装很容易将这些变成一个独特的表示。以前会成为:

9A 8A 7B 6B

我的问题是 - 将后者变回前者列表的最优雅方法是什么?例如,当输入为9A 8A 7B 6B时,输出应为:

9c 8c 7d 6d
9c 8c 7h 6h
9c 8c 7s 6s
9h 8h 7d 6d
9h 8h 7c 6c
9h 8h 7s 6s
9d 8d 7c 6c
9d 8d 7h 6h
9d 8d 7s 6s
9s 8s 7d 6d
9s 8s 7h 6h
9s 8s 7c 6c

我有一些丑陋的代码可以根据具体情况执行此操作,具体取决于有多少个独特的套装。它不会扩展到拥有更多牌的牌。也是在这样的情况下:

7A 7B 8A 8B

它会有重复项,因为在这种情况下A=cB=dA=dB=c相同。

有效解决此问题的优雅方法是什么?我用C编码,但我可以将更高级别的代码转换为C.

4 个答案:

答案 0 :(得分:3)

只有4套西装,所以可能的替换空间真的很小 - 4! = 24例。 在这种情况下,我认为值得尝试提出一些特别聪明的东西。

只需解析字符串“7A 7B 8A 8B”,计算其中不同字母的数量,并根据该数字,根据预先计算的一组替换生成替换。

1 letter -> 4 possible substitutions c, d, h, or s
2 letters -> 12 substitutions like in Your example.
3 or 4 letters -> 24 substitutions.

然后对替换集进行排序并删除重复项。你已经在每个字符串中对标记进行排序,如“7c 8d 9d 9s”,然后对字符串数组进行排序以检测重复项,但这应该不是问题。像“7A 7B 8A 8B”这样的模式也是很好的(像“7A”,“8B”这样的标记是按升序排列的)。

修改

排序的另一种选择可能是,如果排名与两个或多个诉讼相关联,则检测相同的集合并在生成替换时将其考虑在内,但我认为这更复杂。您必须为模式字符串中出现的每个字母创建一组排名。

例如,对于字符串“7A 7B 8A 8B”,字母A,关联的是集合{7,8},并且相同的集合与字母B相关联。那么您必须查找相关的相关集合用不同的字母。在大多数情况下,这些集合只有一个元素,但它们可能有两个,如上例所示。与同一组相关联的字母是可互换的。您可以有以下情况

1 letter no duplicates -> 4 possible substitutions c, d, h, or s
2 letters no duplicates -> 12 substitutions.
2 letters, 2 letters interchangeable (identical sets for both letters) -> 6 substitutions.
3 letters no duplicates -> 24 substitutions.
3 letters, 2 letters interchangeable -> 12 substitutions.
4 letters no duplicates -> 24 substitutions.
4 letters, 2 letters interchangeable -> 12 substitutions.
4 letters, 3 letters interchangeable -> 4 substitutions.
4 letters, 2 pairs of interchangeable letters -> 6 substitutions.
4 letters, 4 letters interchangeable -> 1 substitution.

答案 1 :(得分:2)

我认为采用数组arr和整数n并返回该数组中n个元素的所有可能排列的泛型置换函数在这里很有用。

了解手中存在多少件独特的套装。然后使用实际套装[c, d, h, s]中的那些元素生成所有可能的排列。最后完成每个套装的排列,并将手中的每个未知字母[A, B, C, D]分配给置换值。

Ruby中的以下代码使用给定的手并生成所有套装排列。最重要的工作是通过Array.permutation(n)方法完成的,这对于相应的C程序也应该简化很多事情。

# all 4 suits needed for generating permutations
suits = ["c", "d", "h", "s"]

# current hand
hand = "9A 8A 7B 6B"

# find number of unique suits in the hand. In this case it's 2 => [A, B]
unique_suits_in_hand = hand.scan(/.(.)\s?/).uniq.length

# generate all possible permutations of 2 suits, and for each permutation
# do letter assignments in the original hand
# tr is a translation function which maps corresponding letters in both strings.
# it doesn't matter which unknowns are used (A, B, C, D) since they 
# will be replaced consistently.
# After suit assignments are done, we split the cards in hand, and sort them.
possible_hands = suits.permutation(unique_suits_in_hand).map do |perm|
   hand.tr("ABCD", perm.join ).split(' ').sort
end

# Remove all duplicates
p possible_hands.uniq

以上代码输出

9c 8c 7d 6d
9c 8c 7h 6h
9c 8c 7s 6s
9d 8d 7c 6c
9d 8d 7h 6h
9d 8d 7s 6s
9h 8h 7c 6c
9h 8h 7d 6d
9h 8h 7s 6s
9s 8s 7c 6c
9s 8s 7d 6d
9s 8s 7h 6h

答案 2 :(得分:0)

将套装表示为稀疏数组或列表,将数字表示为索引,将手表示为关联数组

在你的例子中

H [A [07080000] B [07080000] C [00000000] D [00000000]](四张牌的地方)

要获得“真正的”牌总是应用24个排列(固定时间),所以你不必关心手牌A,B,C,D - >的牌数。 c,d,h,s具有以下“技巧”>总是按字母顺序存储>

H1 [c [xxxxxx] d [xxxxxx] s [xxxxxx] h [xxxxxx]]

由于Hands是关联数组,因此重复的排列不会产生两个不同的输出指针。

答案 3 :(得分:0)

#include <stdio.h>
#include <stdlib.h>

const int RANK = 0;
const int SUIT = 1;

const int NUM_SUITS = 4;

const char STANDARD_SUITS[] = "dchs";
int usedSuits[] = {0, 0, 0, 0};

const char MOCK_SUITS[] = "ABCD";

const char BAD_SUIT = '*';

char pullSuit (int i) {
  if (usedSuits [i] > 0) {
    return BAD_SUIT;
  }
  ++usedSuits [i];
  return STANDARD_SUITS [i];
}

void unpullSuit (int i) {
  --usedSuits [i];
}

int indexOfSuit (char suit, const char suits[]) {
  int i;
  for (i = 0; i < NUM_SUITS; ++i) {
    if (suit == suits [i]) {
      return i;
    }
  }
  return -1;
}

int legitimateSuits (const char suits[]) {
  return indexOfSuit (BAD_SUIT, suits) == -1;
}

int distinctSuits (const char suits[]) {
  int i, j;
  for (i = 0; i < NUM_SUITS; ++i) {
    for (j = 0; j < NUM_SUITS; ++j) {
      if (i != j && suits [i] == suits [j]) {
        return 0;
      }
    }
  }
  return 1;
}

void printCards (char* mockCards[], int numMockCards, const char realizedSuits[]) {
  int i;
  for (i = 0; i < numMockCards; ++i) {
    char* mockCard = mockCards [i];
    char rank = mockCard [RANK];
    char mockSuit = mockCard [SUIT];
    int idx = indexOfSuit (mockSuit, MOCK_SUITS);
    char realizedSuit = realizedSuits [idx];
    printf ("%c%c ", rank, realizedSuit);
  }
  printf ("\n");
}

/*
 * Example usage:
 * char** mockCards = {"9A", "8A", "7B", "6B"};
 * expand (mockCards, 4);
 */
void expand (char* mockCards[], int numMockCards) {
  int i, j, k, l;
  for (i = 0; i < NUM_SUITS; ++i) {
    char a = pullSuit (i);
    for (j = 0; j < NUM_SUITS; ++j) {
      char b = pullSuit (j);
      for (k = 0; k < NUM_SUITS; ++k) {
        char c = pullSuit (k);
        for (l = 0; l < NUM_SUITS; ++l) {
          char d = pullSuit (l);
          char realizedSuits[] = {a, b, c, d};
          int legitimate = legitimateSuits (realizedSuits);
          if (legitimate) {
            int distinct = distinctSuits (realizedSuits);
            if (distinct) {
              printCards (mockCards, numMockCards, realizedSuits);
            }
          }
          unpullSuit (l);
        }
        unpullSuit (k);
      }
      unpullSuit (j);
    }
    unpullSuit (i);
  }
}

int main () {
  char* mockCards[] = {"9A", "8A", "7B", "6B"};
  expand (mockCards, 4);
  return 0;
}