추상적 게임을위한 최적의 전략


12

나는 인터뷰에서 다음과 같은 문제를 . (예 : )이 숫자는 이진 표현으로 변환되고 은 설정된 비트 수입니다 . (예 : , )A 0 = 1234 N 1 A 0 = b 100 1101 0010 N = 5A0A0=1234N1A0=b100 1101 0010N=5.

플레이어 1 은 보다 작은 선택합니다 . 비트는 1로 설정해야합니다 (예 : . . (예 ). 이동이 유효한 경우 설정 비트 수를 만족 이전 제한하고있는 경우 여전히 N 동일 .A 0 B 0 B 0 = b 10 0000 0000 = 512 A 1 = A 0B 0 A 1 = 1234 512 = 722 = b 10 1101 0010 B 0 A 1B0A0B0B0=b10 0000 0000=512A1=A0B0A1=1234512=722=b1011010010B0A1

플레이어 2 는 유효한 을 선택하여 에서 계속 한 다음 플레이어 1 은 등에서 계속 됩니다. 왼쪽으로 유효한 움직임이 없으면 플레이어가 패배합니다.B 1 A 2A1B1A2

두 선수가 모두 최적으로 플레이한다고 가정하면 합리적으로 효율적인 방법으로 승리 한 선수를 결정하십시오. (내 문제 정의에서 이것에 대한 제약은 프로그램이 부호있는 32 비트 정수에 맞는 수백만 개의 입력 번호에 대한 솔루션을 제공 할 수 있어야한다는 것입니다.), 솔루션은 필요하지 않습니다. 완전히 분석적입니다.


제가 개인적으로 관심을 갖는 것은 제가받은 120 분 동안 정확성에 대한 피드백을 전혀 얻지 않고 올바른 솔루션을 찾고 구현할 것이라는 기대가 합리적 이었는지 알아내는 것입니다. 또는 이것이 "그들이 전에이 퍼즐을 본 적이 있는지 보자"라는 질문 중 하나 였다면.

나는 합리적인 전략처럼 보이는 것을 구현하기로 선택했기 때문에 실패했습니다. 내 시간이 다했을 때 전체 출력.

돌이켜 보면 나는 무차별 대입 검색을 구현하고 작은 시작 번호에 대한 부분 솔루션을 암기해야했지만 가늠자는 항상 20/20입니다. 그러나 나는 플루 키로 나를 피하는 다른 일반적인 접근법이 있는지 궁금합니다.


설명에서 선택한 동작이 단일 비트를 1로 설정해야한다는 것을 이해하지 못했습니다 (예제의 일부라고 생각했습니다).
jjmontes

@jjmontes-B 번호를 선택하는 첫 번째 규칙으로 표시됩니다. 모든 예는 괄호 밖의 모든 것이 일반적입니다. 그것이 더 명확했을 수있는 제안이 있습니까?
millimoose

1
어쩌면 "플레이어 1이 선택하는 숫자 적은보다는 , 해야한다 1. 단 하나의 비트가 설정되어"? (아마도 그것은 단지 나 였지만, 이것이 제약이라는 것을 깨닫기 위해 @orlp 답변을 읽어야했습니다). A 0B0A0
jjmontes

@Veedrac-Man, 내가 비트 카운트를 합리적으로 빠르게 만들기위한 모든 노력은 낭비가 아니라는 것을 알고 있었다. 작동 하는지 설명하는 대답 은 훌륭 할 것입니다.
millimoose

@millimoose Bitcount는 대부분의 최신 CPU를위한 하드웨어에 있습니다!
Veedrac

답변:


21

우리가 2의 거듭 제곱 만 빼고 팝 카운트를 변경할 수 없다면 다른 숫자가 10 인 위치에서 을 빼야한다는 것을 스스로 깨달으십시오 . 그 결과는 항상 그 위치에서 01 이며 숫자는 다른 곳에서 변경되지 않습니다.011001

1001

따라서 게임에서 유일하게 결정적인 요소는 모든 스왑이 올바른 상태에 도달하는 데 걸리는 스왑의 수이며,이기거나지는 전략은 없습니다. 스왑 수의 패리티는 유일한 결정 요인입니다.

1

i1101kki

i = 0
k = 0
total = 0
while n > 0:
    if n & 1:
       total += k - i
       i += 1
    n >>= 1
    k += 1

totalO(logn)


이것은 옳은 것처럼 보입니다. 핸드 인 후이 접근법의 비트와 조각을 우연히 발견했습니다. 코딩을 시작할 때 총을 튀어 나와서 거위 추적에 빠져 들었다고 생각합니다.
millimoose가

이것에 대해 생각할 때, 전략이 중요하지 않다는 사실은 구현에 다소 모호한 버그가 있거나 게임을 올바르게 수행하는 다른 구현과 동일한 결과를 생성했음을 의미합니다.
millimoose

5

이러한 문제를 해결하는 한 가지 방법은 다음과 같습니다.

  • 제안하는 "메모리 화 된 무차별 대입"방식을 사용하여 몇 가지 간단한 값에 대한 솔루션을 찾으십시오.

  • 답을 추측하십시오 (어떤 포지션이 이기고 어느 것이 이기고 있는지).

  • 답을 증명해보십시오. 성공하면 좋습니다. 그렇지 않으면, 반례를 찾아 다른 답을 추측하는 데 사용하십시오. 여기서 몇 가지 사례를 더 해결하는 것이 도움이 될 수 있습니다.

시간이 얼마나 걸리는지 말하기는 정말 어렵습니다. 그러나 인터뷰에서 반드시 해결책을 찾지 못할 수도 있습니다. 오히려, 인터뷰는 방법을 알고 싶어요 접근 문제를 해결, 당신은 할 관리 어떻게 진행.


예, 출력이 잘못되어 시간이 부족하여 거부했습니다.
millimoose가

메모 된 무차별 대입 접근 방식은 전략에 대한 지름길이 없기 때문에 정확했을 것입니다. 그러나, 나는 또한-느리게 느릴 것이라고 생각했으며, 어리석은 양의 메모리를 사용하지 않으면 많은 도움이되지 않기 때문에 메모 작업이 너무 많은 작업을 수행했을 수 있습니다. 아니면 아닐 수도 있습니다. 나중에 시스템에서 지우려면 나중에 시도하겠습니다.
millimoose가

5

@orlp의 대답에서 시작 위치에서 끝 위치까지의 변위 합계의 패리티가 필요하다는 점에 유의하십시오. 주석을 달자 :

       9876543210
       9 76 4  1    (positions at start)
start: 1011010010
end:   0000011111
            43210   (positions at end)

그래서 우리는 원한다

  ((1 - 0) + (4 - 1) + (6 - 2) + (7 - 3) + (9 - 4)) & 1
= ((1 + 4 + 6 + 7 + 9) - (0 + 1 + 2 + 3 + 4)) & 1
= ((1 + 0 + 0 + 1 + 1) - (0 + 1 + 0 + 1 + 0)) & 1

첫 번째 부분은 홀수 위치에있는 비트 수의 패리티 일뿐입니다. 부호없는 최대 정수를 취하여 0b11로 나누고 부정함으로써이를 숨길 수 있습니다.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (0 + 1 + 0 + 1 + 0)) & 1

두 번째 부분은 의 비트 수의 절반 의 패리티입니다 x.

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (bitcount(x) >> 1)) & 1

bitcount중 하드웨어 사용할 수있는 popcnt명령을, 또는 단지 중 마지막이나 마지막에서 두 번째 비트가 함께 필요하다는 것을 이용하여 수동으로 구현 될 수있는 이 같은 빠른 감소 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.