자연수 찾기 문제

면접관이 자연수 하나를 생각할테니 그 수를 질문을 통해 맞춰보라고 합니다. 단, 질문에 대한 답은 예/아니오밖에 존재할 수 없으며 면접관은 거짓말을 하지 않습니다.
최소한의 질문으로 수를 맞추려면 어떤 전략을 짜야 할까요?

생각한 수가 X가 맞냐는 질문을 해서 맞다는 답이 나왔을 경우 최종적으로 수를 맞췄다고 인정합니다. 예를 들면

Q: 수가 1이 맞나요?

A: No

Q: 수가 2가 맞나요?

A: Yes

위와 같은 대화에선 2번만에 수를 맞춘 것입니다.

풀이 가리기

저는 이런 방법으로 풀었습니다.

  • 바이너리 서치가 가장 빠를 것 같지만 수의 범위가 제한이 없으니 1의 자리부터 거꾸로 해나가면 된다.
  • 언제 끝날지는 모르니 중간중간 계산을 해서 그 수가 맞는 지 확인을 해봐야 한다.

제가 생각한 시나리오는 다음과 같습니다.

Q: 생각한 수를 2진수로 변환했을 때 맨 오른쪽 자릿수가 1입니까?

A: Yes

Q: 그러면 생각한 수가 1입니까?

A: No

Q: 생각한 수를 2진법으로 변환했을 때 오른쪽에서 2번째 자릿수가 1입니까?

A: Yes

Q: 그러면 생각한 수가 3입니까?

A: Yes

그래서 다음과 같은 소스가 나왔습니다.

def is_number(orig, guess):
    if orig == guess:
        return True
    return False


def is_number_nth_field_one(orig, n):
    num = orig / (2 ** (n - 1))
    if num % 2:
        return True
    return False


def guess_number(num):
    count = 0
    n = 1
    guessing = 0
    correct = False
    while not correct:
        count += 1
        if is_number_nth_field_one(num, n):
            guessing += 2 ** (n - 1)
		count += 1
		if is_number(num, guessing):
			correct = True
        n += 1
    return count

if __name__ == '__main__':
    total = 0
    for i in xrange(1, 1000001):
        count = guess_number(i)
        total += count
        print "guess %d, count: %d, total: %d" % (i, count, total)

    print "Total: %d" % total

위의 방법으로 1부터 1000000까지의 수를 모두 돌려보면 총 37902890번의 질문을 합니다.
하지만 뭔가 더 질문의 수를 줄일 수 있을 것 같았습니다.
잘 살펴보면 알겠지만, 쓸모 없는 질문이 있습니다. N번째 자릿수를 물어봤을 때 0이었다면 생각한 수를 확인하는 과정에서 아까 아니라고 한 수를 또 물어봅니다. 다음과 같은 대화가 오가는거죠.

Q: 생각한 수를 2진수로 변환했을 때 맨 오른쪽 자릿수가 1입니까?

A: Yes

Q: 그러면 생각한 수가 1입니까?

A: No

Q: 생각한 수를 2진법으로 변환했을 때 오른쪽에서 2번째 자릿수가 1입니까?

A: No

Q: 그러면 생각한 수가 1입니까?

A: No

그래서 자릿수가 0일 땐 질문을 하지 않고 바로 다음 자리수로 넘어가게 했습니다.

def is_number(orig, guess):
    if orig == guess:
        return True
    return False


def is_number_nth_field_one(orig, n):
    num = orig / (2 ** (n - 1))
    if num % 2:
        return True
    return False


def guess_number(num):
    count = 0
    n = 1
    guessing = 0
    correct = False
    while not correct:
        count += 1
        if is_number_nth_field_one(num, n):
            guessing += 2 ** (n - 1)
			count += 1
			if is_number(num, guessing):
				correct = True
        n += 1
    return count

if __name__ == '__main__':
    total = 0
    for i in xrange(1, 1000001):
        count = guess_number(i)
        total += count
        print "guess %d, count: %d, total: %d" % (i, count, total)

    print "Total: %d" % total

아까랑 아주 살짝 다르죠. 이번에는 총 28836444번 질문을 했습니다. 아까보다 훨씬 줄었죠.

이것보다 더 좋은 해법이 있는지는 모르겠습니다. 더 좋은 답이 있다면 알려주세요.