5 카드 포커 핸드를 나타냅니다


11

한 장의 카드는 52 장입니다. 한 장의 카드는 52 장 중 5 장입니다 (중복 할 수 없습니다).

5 장의 카드 핸드를 나타내는 최소 비트 수는 어떻게됩니까?
손은 주문에 의존하지 않습니다 (KQ = QK). 64329 = 96432

예, 52 비트를 사용할 수 있습니다. 그것은 많은 카드의 손을 나타낼 수 있습니다.

핸드가 정확히 5 장인 경우 52 비트 미만으로 표현할 수있는 방법이 있습니다.

단일 카드는 6 비트 = 64로 표현 될 수 있으므로 6 비트 * 5 카드 = 30 비트 만 사용할 수 있습니다. 그러나 그것은 순서에 달려 있습니다. 나는 정렬 할 수 있었고 이것이 작동해야합니다. 그래도 작동하지 않으면 알려주십시오.

키를 32 비트 이하로 가져오고 5 카드 튜플을 정렬 할 필요가없는 방법이 있습니까?

이것은 포커 시뮬레이션을위한 것이며 정렬은 손을 생성하는 것보다 많은 오버 헤드가 될 것입니다. 각 손의 상대 값이있는 사전이 있으면 두 가지 간단한 조회와 두 손의 값을 비교하는 비교입니다. 손을 먼저 정렬 해야하는 경우 두 조회와 비교에 비해 큰 손입니다. 시뮬레이션에서 수백만을 비교합니다. 시뮬레이션에서 손을 떼지 않을 것입니다. 정렬은 52 51 50 49 47 전에 52 51 50 49 48처럼 간단하지 않습니다. 직선 플러시 쿼드를 가질 수 있습니다 ....

2598960 개의 5 장의 카드를 사용할 수 있습니다. 이것이 행의 수입니다. 열쇠는 5 장의 카드입니다. 32 비트 또는 카드를 먼저 정렬 할 필요가없는 키를 얻고 싶습니다.

손 묶는 수만큼 목록을 주문할 수는 없습니다. 정장은 스페이드, 클럽, 다이아몬드 및 하트입니다. 7c 8c 2d 3d 4s = 7s 8s 2c 3c 4h. 많은 관계가 있습니다.

다음 단계는 64 비트이며 키 크기를 두 배로 늘리지 않고 정렬을 수행합니다.

나는 SortedSet<int> quickSort = new SortedSet<int>() { i, j, k, m, n };작업 시간을 테스트하고 두 배로 늘 렸지만 여전히 할 수 있습니다.

더 복잡해집니다. 보트를 5 대 2 (22255)로 표현할 수 있어야합니다. 그래서 그것들을 분류하면 깨집니다. 나는 당신이 말할 것이라고 알고 있지만 그것은 빠릅니다. 예, 빠르고 사소하지만 최대한 빨리 필요합니다.

허용되는 답변의 C # :

private int[] DeckXOR = new int[] {0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,
                                    0x00000080,0x00000100,0x00000200,0x00000400,0x00000800,0x00001000,0x00002000,
                                    0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000,0x00100000,
                                    0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x07fe0000,
                                    0x07c1f000,0x0639cc00,0x01b5aa00,0x056b5600,0x04ed6900,0x039ad500,0x0717c280,
                                    0x049b9240,0x00dd0cc0,0x06c823c0,0x07a3ef20,0x002a72e0,0x01191f10,0x02c55870,
                                    0x007bbe88,0x05f1b668,0x07a23418,0x0569d998,0x032ade38,0x03cde534,0x060c076a,
                                    0x04878b06,0x069b3c05,0x054089a3};
public void PokerProB()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    HashSet<int> cardsXOR = new HashSet<int>();
    int cardXOR;
    int counter = 0;
    for (int i = 51; i >= 4; i--)
    {
        for (int j = i - 1; j >= 3; j--)
        {
            for (int k = j - 1; k >= 2; k--)
            {
                for (int m = k - 1; m >= 1; m--)
                {
                    for (int n = m - 1; n >= 0; n--)
                    {
                        counter++;
                        cardXOR = DeckXOR[i] ^ DeckXOR[j] ^ DeckXOR[k] ^ DeckXOR[m] ^ DeckXOR[n];
                        if (!cardsXOR.Add(cardXOR))
                            Debug.WriteLine("problem");
                    }
                }
            }
        }
    }
    sw.Stop();
    Debug.WriteLine("Count {0} millisec {1} ", counter.ToString("N0"), sw.ElapsedMilliseconds.ToString("N0"));
    Debug.WriteLine("");
}

4
길이가 5 인 목록을 정렬하는 데 사용할 수있는 수동 코딩 정렬 알고리즘을 사용하십시오. 이는 현재 사용중인 라이브러리 함수보다 빠를 수 있습니다.
Yuval Filmus

1
"정렬하지 않다" 고 말하는지 모르겠습니다 . 정렬 간단합니다-각 카드를 1에서 52까지의 숫자로 변환하여 손이 (길이 5) 카드 목록으로 표시되도록하십시오. 그 목록을 정렬하십시오. 그것은 Yuval이 언급했듯이 매우 빠른 속도로 수행 할 수있는 5 개의 정수 목록을 정렬하는 문제입니다. 너무 느리다고 가정하기 전에 측정하는 것이 좋지만, 그런 목록을 정렬하는 것은 매우 빠르며 캐시에 닿지 않는 임의 액세스 메모리 읽기보다 빠를 수도 있습니다.
DW

@dw 예 정렬은 간단하지만 내가하고있는 일은 (수백만 번) 간단합니다. 나는 테스트했고 일종의 시간을 두 배로 늘렸다.
paparazzo

1
@Paparazzi 아니요, Yuval은 1에서 52 사이의 5 개의 숫자를 정렬하도록 특별히 조정 된 자체 정렬 루틴을 작성하도록 지시합니다. 라이브러리 루틴을 사용하려고 시도했습니다. 이보다 훨씬 일반적이며 재귀 적 특성 때문에 느립니다. quicksort를 사용하면 짧은 목록에서 매우 비효율적입니다.
David Richerby

실제로, <= 16 비트가 아닌 대부분의 항목은 32 비트 일 수도 있습니다. 따라서 최소 23 비트가 필요하므로 <= 32 비트를 사용하는 모든 인코딩이 가능합니다. 카드 당 사소한 6 비트 * 5 카드 인코딩은 충분히 작동합니다. 한 가지주의 사항이 있습니다. 23 비트 배열 인덱스가 32 비트 배열 인덱스보다 훨씬 낫습니다.
MSalters

답변:


10

를 코드 하자 . 의 패리티 검사 행렬 A는 컬럼 XOR이 소멸의 최소 수 인 것을 비트 매트릭스 예 . 개의 열을 나타냅니다 . 각 를 길이 비트 의 이진수로 식별 할 수 있습니다 . 이 숫자 중 에서 까지 의 XOR 은 절대 이 아닙니다 . 이를 사용하여 손 를 . 여기서C[52,25,11]C27×521152A1,,A52Ai271100a,b,c,d,eAaAbAcAdAeXOR입니다. 실제로 이것은 분명히 순서에 의존하지 않으며 두 손 충돌하면 두 해시 값을 XOR하면 XOR이 0 인 숫자가 표시됩니다.H1,H2102|H1H2|10

Bob Jenkins는 자신의 사이트 에서 이러한 코드를 설명 하고 그로부터 배열을 추출 할 수 있습니다.

0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,
0x00000080,0x00000100,0x00000200,0x00000400,0x00000800,0x00001000,0x00002000,
0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000,0x00100000,
0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x07fe0000,
0x07c1f000,0x0639cc00,0x01b5aa00,0x056b5600,0x04ed6900,0x039ad500,0x0717c280,
0x049b9240,0x00dd0cc0,0x06c823c0,0x07a3ef20,0x002a72e0,0x01191f10,0x02c55870,
0x007bbe88,0x05f1b668,0x07a23418,0x0569d998,0x032ade38,0x03cde534,0x060c076a,
0x04878b06,0x069b3c05,0x054089a3

처음 27 개의 벡터는 단지 27 개의 해밍 가중치 1이므로,이 구성이 올바른지 확인하기 위해서는 가능한 모든 사소한 것을 고려하면 충분합니다 XOR에 항상 해밍 가중치가 10 이상인지 확인하는 마지막 25 개의 숫자 조합. 예를 들어 첫 번째 숫자 0x07fe0000의 해밍 가중치는 정확히 10입니다.252271=2251


나는 정확하게 따르지 않습니다. 손에 몇 비트가 필요합니까?
paparazzo

27 비트가 필요합니다. 더 많은 수의 비트를 사용할 수 있습니다.
Yuval Filmus

감사. 나는 테스트했으며 숫자는 독특하고 32 비트 미만입니다. 숫자에서 5 장의 카드를 파생시킬 수 있습니까? 그냥 물어 보는 것이 좋지 않다면.
paparazzo

예, 간단한 선형 대수입니다. 올바른 행렬을 사용하여 길이가 52 인 5의 벡터를 되 찾을 수 있습니다. 내가 알아낼 게
Yuval Filmus

13

크기가 인 세트가 있으면 비트를 사용하여 세트의 요소를 나타낼 수 있습니다 . 당신은 2598960 가능한 5 카드 핸드가 있다고 말합니다. 즉, 5 카드 핸드는 비트 만 사용하여 표현할 수 있습니다 . 22 비트는 30 비트보다 상당히 짧습니다.lg n lg 2598960 = 22nlgnlg2598960=22

표현은 어떻게 작동합니까? 다른 트레이드 오프와 함께 다양한 옵션이 있습니다. 아래에 두 가지를 나열합니다.

하드 코딩 된 사전

이 경우 가능한 5 장의 카드 핸드 수는 2598960 개의 핸즈를 모두 나열하는 하드 코딩 된 사전을 가질 수있을 정도로 작아서 사전에 이진으로 표시되는 색인으로 손을 나타냅니다.

즉, 사전은 손의 정렬 된 목록 일 수 있습니다. 각 핸드는 핸드에있는 5 장의 튜플입니다. 이진 검색을 사용하여 사전에서 손을 찾아 해당 색인을 찾을 수 있습니다. 색인이 주어지면 해당 손을 찾을 수 있습니다. 또는 사전을 손에서 색인으로 매핑하는 해시 맵으로 저장할 수 있습니다. 인덱스는 0에서 2598959 사이의 정수이므로 23 비트를 사용하여 나타낼 수 있습니다.

이 접근 방식은 작동하고 프로그래밍하기가 매우 간단하지만 공간이 많이 낭비됩니다 (프로그램 실행 파일 크기).

랭킹 / 랭킹 해제

또는 관심이 있다면 더 좋은 방법이 있습니다. 예를 들어 다음 참조 중 하나를 참조하십시오.

일반적인 주제는 "조합 순위 지정 (및 등급 지정 해제)"으로 알려져 있습니다. 이들은 구현하고 이해하기가 조금 더 복잡하지만 프로그램에 하드 코딩 된 사전을 포함시킬 필요는 없습니다.


질문을 업데이트하겠습니다. 예, 2598960 개의 손이 있습니다. 사전에는 많은 행이 있습니다. 내 문제는 열쇠의 생성입니다. 5 장의 카드에서 사전 검색을 수행하기위한 키를 생성해야합니다.
paparazzo

@Paparazzi, 사전 접근 방식을 사용하는 경우 손 핵심입니다. 즉, 열쇠는 손에 든 카드의 5 튜플입니다 (정렬 된 순서로). 사전을 키로 사용하여 해시 테이블로 사전을 저장할 수 있습니다. 사전의 메모리 비용이 마음에 들지 않으면 순위 / 랭킹 제거와 같은 다른 방법을 사용하십시오.
DW

예, 정렬하면 30 비트 키를 얻을 수 있다는 것을 알고 있습니다. 5 카드 튜플을 정렬하지 않고 키를 32 비트 이하로 가져올 수있는 방법이 있는지 궁금합니다. 나는 순위와 순위를 살펴볼 것입니다.
paparazzo

나는 순위 / 순위를 따르지 않지만 감사합니다. 시도하고 알아낼 것입니다. 또한 관계의 가능성이 있습니다. 많은 관계가 있습니다.
paparazzo


3

5 개 항목을 정렬하고 일부 프로세서에서 비교없이 중복 항목을 동시에 확인할 수 있습니다. 프로세서에 가장 높은 비트 세트의 위치를 ​​결정하는 빠른 명령과 n 번째 비트 세트 만있는 숫자를 계산하는 빠른 명령이 있다고 가정하십시오. .

비트 (n)을 정확히 n 번째 비트가 설정된 숫자로 둡니다. 가장 높은 비트 (x)는 x = 0 인 경우 지정되지 않은 값을 사용하여 숫자 x에 설정된 가장 높은 비트의 번호가되도록합니다. x ^ y는 x와 y의 배타적입니다.

5 개의 숫자 a, b, c, d 및 e (각각 0에서 51까지)가 주어지며 손에있는 5 장의 카드를 나타냅니다.

x = 비트 (a) ^ 비트 (b) ^ 비트 (c) ^ 비트 (d) ^ 비트 (e)라고하자.

A = highest_bit (x)라고하고 x를 x ^ 비트 (A)로 변경하십시오.

B = 최고 비트 (x)로하고 x를 x ^ 비트 (B)로 변경합니다.

C = highest_bit (x)라고하고 x를 x ^ 비트 (C)로 변경하십시오.

D = 최고 비트 (x)로하고 x를 x ^ 비트 (D)로 변경합니다.

E = highest_bit (x)라고하자.

x = 0이면 숫자 a, b, c, d 및 e에 중복이있었습니다. 그렇지 않으면 A * 비트 (24) + B * 비트 (18) + C * 비트 (12) + D * 비트 (6) + E를 손의 인코딩으로 사용하십시오. 여기서 A, B, C, D 및 E는 위와 같이 정의되었습니다. 이것은 매우 효율적인 방법으로 정렬을 수행하면서 손을 30 비트 문자열로 인코딩합니다.


이것은 52 비트를 사용합니까?
paparazzo

@ 파파라치 마지막 단락을 다시 살펴보십시오. 더 선명하게 보이도록 편집했습니다.
DW

1
64 비트 CPU가 필요하지만 최종 결과는 30 비트입니다.
Yuval Filmus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.