분수에 대한 이진 검색

Nov 24 2020

저는 Sedgewick의 Algorithms 책에서 다음과 같은 연습 문제를 해결하려고합니다.

숫자가 x보다 작습니까? 형식의 로그 수를 사용하는 방법을 고안하십시오. 0 <p <q <N과 같은 유리수 p / q를 찾는 것입니다. 힌트 : 분모가 N보다 작은 두 분수는 1 / N ^ 2보다 크게 다를 수 없습니다.

이진 검색에 필요한 간격이] 0, 1 [이라는 것을 알고 있지만 무엇을 봐야하는지, N이 무엇인지 잘 모르겠습니다. 누군가 나에게 설명해 주시겠습니까?

답변

btilly Nov 25 2020 at 16:48

힌트를 무시하고 훨씬 더 어려운 문제에 대한 해결책이 있습니다.

즉 찾을 수 있는 분자 / 분모의 절대 값에 바인딩 로그와, 그게 얼마나 큰 사전에 알지 못하고, 이진 검색에 의해 합리적입니다.

Stern-Brocot 트리의 이진 검색입니다.

class GuessState:
    def __init__ (self):
        self.direction = None
        self.start = [0, 1]
        self.bound = [0, 0]
        self.multiple_upper = 1
        self.multiple_lower = 1
        self.is_widening = True
        self.is_choosing = None

    def next_guess (self):
        if self.is_widening:
            multiple = self.multiple_upper
        else:
            multiple = (self.multiple_lower + self.multiple_upper) // 2
        return (self.start[0] + multiple * self.bound[0], self.start[1] + multiple * self.bound[1])

    def add_response (self, response):
        next_level = False
        if self.direction is None:
            if 0 < response:
                self.bound[0] = 1
                self.direction = 1
            else:
                self.bound[0] = -1
                self.direction = -1
            self.is_choosing = True
            return
        elif self.is_choosing:
            if self.direction * response < 0:
                # Reverse direction.
                self.direction = - self.direction
                (self.start, self.bound) = (self.bound, self.start)
            self.multiple_upper = 2
            self.is_choosing = False
        elif self.is_widening:
            if 0 < response * self.direction:
                self.multiple_lower = self.multiple_upper
                self.multiple_upper += self.multiple_upper
            else:
                self.is_widening = False
                if self.multiple_lower + 1 == self.multiple_upper:
                    next_level = True
        elif self.multiple_lower + 1 < self.multiple_upper:
            if 0 < self.direction * response:
                self.multiple_lower = (self.multiple_lower + self.multiple_upper) // 2
            else:
                self.multiple_upper = (self.multiple_lower + self.multiple_upper) // 2
        else:
            next_level = True

        if next_level:
            next_start = (self.start[0] + self.multiple_lower * self.bound[0], self.start[1] + self.multiple_lower * self.bound[1])
            next_bound = (self.start[0] + self.multiple_upper * self.bound[0], self.start[1] + self.multiple_upper * self.bound[1])
            self.start = next_start
            self.bound = next_bound
            self.multiple_lower = 1
            self.multiple_upper = 1
            self.is_choosing = True
            self.is_widening = True

def guesser (answerer):
    state = GuessState()
    response = answerer(state.next_guess())
    while response != 0:
        state.add_response(response)
        response = answerer(state.next_guess())
    return state.next_guess()

def answerer (answer):
    def compare (guess):
        val = guess[0] / guess[1]
        print(f"Comparing answer {answer} to guess {val} ({guess[0]}/{guess[1]})")
        if val < answer:
            return 1
        elif answer < val:
            return -1
        else:
            return 0
    return compare

print(guesser(answerer(0.124356)))