Найти в Дзене
Я, Golang-инженер

#22. Угадываем число в диапазоне - мощный алгоритм

Оглавление

Это статья об основах программирования. На канале я рассказываю об опыте перехода в IT с нуля, структурирую информацию и делюсь мнением.

Хой, джедаи и амазонки!

В одном из уроков Skillbox мы упражнялись в сохранении информации в лог-файл. Задача ставилась так: "Помните, мы делали программу, которая угадывает число в диапазоне до 100? Давайте улучшим её и сделаем вывод в лог-файл, для этого поставьте видео на паузу и..."

Мы делали подобную программу. Но не до 100, а от нуля до десяти - через условия. Циклов тогда ещё не освоили.

Суть программы - она предлагает вариант вашего загаданного числа, а вы говорите "больше", "меньше" или "угадано". И нужно, чтобы программа угадывала с минимально возможного числа попыток.

Вот поставил я видео на паузу и уже несколько вечеров пытаюсь собрать эту программу. Собрал, решил улучшить. В итоге что вышло:

  • Программа запрашивает максимальное значение из диапазона. Т.е. вы можете загадать 25, но в диапазоне (от нуля) до 100 000 000. А можете загадать 324 и выбрать диапазон до 999 - какой захотите, такой и зададите.
  • Программа посчитает максимальное число попыток, которое ей потребуется, чтобы отгадать ваше число. Например, для диапазона от 0 до 100 000, программе потребуется 17 попыток.
  • Программа рассчитывает ваше число через алгоритм, используя ваши подсказки, и сохраняет отчёт в лог-файл.
Пример начала работы программы
Пример начала работы программы

Далее покажу код, а после - пару слов о нескольких функциях и решениях. Для решения задачи мне понадобилось четыре пакета:

  1. fmt;
  2. math (округление, логарифм);
  3. os (создан лог-файл);
  4. strconv (конвертация числа в строку).

КОД

package main

import (
"fmt"
"math"
"os"
"strconv"
)

func main() {
fmt.Println("Я угадаю твоё число с минимального числа попыток. Отвечай +, - или напиши Да. + означает, что ваше число больше моего, - что ваше число меньше моего. И Да означает, что я угадал ваше число")
fmt.Println("-------------")
fmt.Println("Загадайте число и введите диапазон, в котором мы угадываем: от 0 до...")

var max int
fmt.Scan(&max)
maxTry := math.Ceil(math.Log2(float64(max)))
fmt.Printf("Угадаю ваше число максимум с %v попыток.\n", maxTry)
maxNum := max
minNum := 1
midNum := maxNum / 2

file, err := os.Create("Lof-file.txt")
if err != nil {
fmt.Println("Ну удалось создать файл", err)
return
}
defer file.Close()
var sLog string

file.WriteString(strconv.Itoa(midNum))
file.WriteString("\n")

OutLoop:
for i := 1; ; i++ {
fmt.Println("Попытка №:", i)
fmt.Printf("Ваше число %v? \n", midNum)

var answerHuman string
fmt.Scan(&answerHuman)

switch answerHuman {
case "Да":
fmt.Print("Угадал!")
sLog = "Угадал!"
file.WriteString(sLog)

break OutLoop
case "-":
maxNum = midNum
midNum = (minNum + maxNum) / 2
if maxNum == midNum {
midNum--
}
case "+":

minNum = midNum
midNum = (minNum + maxNum) / 2
if minNum == midNum {
midNum++
}
default:
fmt.Print("Ожидался другой ответ")
sLog = "Ожидался другой ответ"
file.WriteString(sLog)
return
}
file.WriteString(strconv.Itoa(midNum))
file.WriteString("\n")
if midNum > max {
fmt.Println("Ожидалось, что загаданное число не больше верхнего значения диапазона.")
break
}
}
fmt.Println(file.Name())
}

Примечания по коду

Далее я рассказываю о лайфхаках, которые считаю интересными

Определяем максимальное число попыток, чтобы угадать число

Чтобы найти максимальное число попыток, я использвал код:

var max int
fmt.Scan(&max)
maxTry := math.Ceil(math.Log2(float64(max)))
fmt.Printf("Угадаю ваше число максимум с %v попыток.\n", maxTry)

Что здесь интересного? В третьей строке происходит магия. Если не вдаваться в математические детали, то 2 в степени количества максимальных попыток угадывания равняется верхнему пределу диапазона угадывания.

Пример: 2^8=256 (столько же было мегабайт ОЗУ на моём первом ПК). Т.е. в диапазоне от 1 до 256, нужно не больше 8 попыток для угадывания при заданном алгоритме.

Чтобы найти эту восьмёрку, нужно воспользоваться логарифмом по основанию 2:

8 = log2(256).

Т.е. сперва задаём переменную max, считываем её - это максимальное значение диапазона для поиска числа пользователя.

При инициализации переменной max я использовал тип данных int, т.к. он мне нужен дальше. А для расчёта логарифма, нужно переконвертировать переменную типа int во float64. Так появилось выражение

math.Log2(float64(max))

Далее нужно округлить значение, т.к. если этого не сделать, то в выводе в терминал получим примерно следующую картину:

"Нужно 7,395452358345 попыток, чтобы угадать ваше число". Соответственно, нас это не устроит и нужно округлить число. Проблема в том, что привычная функция Round округляет по правилам математики, а нам нужно округление в большую сторону. Для этого используем функцию Ceil пакета math.

math.Ceil(math.Log2(float64(max)))

Вывод текста в лог-файл

Чтобы сохранить цифры в лог-файл, я воспользовался функцией Itoa (integer to ASCII, пришло из языка С/UNIX) пакета strconv.

file.WriteString(strconv.Itoa(midNum))
file.WriteString("\n")

Затем добавил перенос строки. Возможно можно сделать это в одну строчку, просто не нашёл нужного синтаксиса. Пока получилось так.

Остальное

Дальше, на мой взгляд, просто математика. Единственно, интересен форматированный вывод, постепенно осваиваюсь с ним:

fmt.Printf("Ваше число %v? \n", midNum)

Очень полезно бывает освоить хотя бы базовые команды форматированного вывода. А ещё по началу трудно запомнить, как сделать перенос строки - впишите \n (слеш не тот, что рядом с num lock).

Вот так, продолжаю изучать Golang. Немножко зависаю на темах обучения, не рвусь вперёд - хочу получше освоить каждый пакет. На очереди strings:

  • Сравнение двух строк;
  • Проверка, входит подстрока в строку, или нет;
  • Проверка, что строка начинается с префикса или заканчивается суффиксом;
  • Копирование строк заданное число раз и пр.

По этой реферальной ссылке друга ты можешь купить курс SkillBox. Для тебя - огромная скидка, для меня - кешбек.

PS Дай знать, если совершишь покупку - иногда этим ребятам нужно напоминать о покупке по реферальной программе.

José M. Reyes https://unsplash.com/photos/0GBxtiFvzXE
José M. Reyes https://unsplash.com/photos/0GBxtiFvzXE

Бро, ты уже здесь? 👉 Подпишись на канал «Я, Golang-инженер», будем изучать IT вместе 👨‍💻👩‍💻👨‍💻