Recherche binaire d'une fraction

Nov 24 2020

J'essaye de résoudre un exercice du livre Algorithmes de Sedgewick qui va comme suit:

Concevez une méthode qui utilise un nombre logarithmique de requêtes de la forme Le nombre est-il inférieur à x? pour trouver un nombre rationnel p / q tel que 0 <p <q <N. Indice: Deux fractions avec des dénominateurs inférieurs à N ne peuvent pas différer de plus de 1 / N ^ 2.

Je suis conscient que l'intervalle dans lequel je dois rechercher binaire est] 0, 1 [mais je ne suis pas sûr de ce que je devrais rechercher et de ce que N est. Quelqu'un peut-il me l'expliquer?

Réponses

btilly Nov 25 2020 at 16:48

Ignorant l'indice, voici une solution à un problème beaucoup plus difficile.

À savoir trouver n'importe quel rationnel par recherche binaire, avec une borne logarithmique sur la valeur absolue du numérateur / dénominateur, sans savoir à l'avance sa taille.

Il s'agit d'une recherche binaire de l'arbre 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)))