Выберите 4 случайных печати - PullRequest
1 голос
/ 18 марта 2020

Итак, у меня проблема с моим кодом. Программа должна выбрать один из 4-х printfs и распечатать его в терминале. Я новичок в этом, так что сожалею об этом.

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

int main () {
    setlocale (LC_ALL, "Portuguese");
    int opcao;
    opcao = rand() % 3 + 1;

    if (opcao == 0) {
        printf ("\nA opção sorteada foi a de que o 1º classificado atual será o campeão (FC Porto)");
    }

    if (opcao == 1) {
        printf ("\nA opção sorteada foi a de que o 1º classificado na 1ª volta será o campeão (SL Benfica)");
    }

    if (opcao == 2) {
        printf ("\nA opção sorteada foi a de que Porto e Benfica farão um jogo em campo neutro para determinar o campeão!");
    }

    if (opcao == 4) {
        printf ("\nFoi sorteada a opção de que não haverá campeão esta época");
    } 


    return 0;
}

Это мой код, но я выбираю только один и тот же printf во все времена.

Ответы [ 4 ]

2 голосов
/ 18 марта 2020

Используйте библиотеку <random> вместо устаревшей и подверженной ошибкам std::rand (использование оператора по модулю для получения случайного целого числа в диапазоне - распространенная ошибка ) , См. Почему новая случайная библиотека лучше, чем std :: rand ()? для получения дополнительной информации.

#include <iostream>
#include <random>

int main()
{
    std::mt19937 engine{std::random_device{}()};
    std::uniform_int_distribution<int> dist{0, 3};

    switch (dist(eng)) {
    case 0:
        std::cout << "...\n";
        break;
    case 1:
        std::cout << "...\n";
        break;
    case 2:
        std::cout << "...\n";
        break;
    case 3:
        std::cout << "...\n";
        break;
    }
}

Здесь мы сначала создаем std::mt19937 двигатель, который производит равномерно распределенные целые числа в полуоткрытом диапазоне [0, 2 32 ), и заполняет его, используя std::random_device, который должен генерировать недетерминированный c номер (может быть реализован, например, с использованием системного времени). Затем мы создаем std::uniform_int_distribution для сопоставления случайных чисел, сгенерированных механизмом, с целыми числами в включающем интервале [0, 3], вызывая его с механизмом в качестве аргумента.


Это можно обобщить, взяв для печати диапазон строк:

template <typename RanIt, typename F>
decltype(auto) select(RanIt begin, RanIt end, F&& f)
{
    if (begin == end) {
        throw std::invalid_argument{"..."};
    }

    thread_local std::mt19937 engine{std::random_device{}()};

    using index_t = long long; // for portability
    std::uniforn_int_distribution<index_t> dist{0, index_t{end - begin - 1}};

    return std::invoke(std::forward<F>(f), begin[dist(engine)]);
}

int main()
{
    const std::array<std::string, 4> messages {
        // ...
    };
    select(messages.begin(), messages.end(),
           [](const auto& string) {
               std::cout << string << '\n';
           });
}

Здесь мы берем пару итераторов произвольного доступа и вызываемый объект для поддержки выбора элемента из произвольного произвольно доступного диапазона и выполнения произвольной операции с ним.

  • Сначала мы проверяем, является ли диапазон пустым, в этом случае выбор невозможен, и throw сообщает об ошибке исключение .

  • Затем мы создаем std::mt19937 движок thread_local (то есть каждый поток имеет свой собственный движок), чтобы предотвратить скачки данных . Состояние механизма поддерживается между вызовами, поэтому мы запускаем его только один раз для каждого потока.

  • После этого мы создаем std::uniform_int_distribution для генерации случайного индекса. Обратите внимание, что мы использовали long long вместо typename std::iterator_traits<RanIt>::difference_type: std::uniform_int_distribution гарантированно работает только с short, int, long, long long, unsigned short, unsigned int, unsigned long и unsigned long long, поэтому, если difference_type равно signed char или расширенный целочисленный тип со знаком, это приводит к неопределенному поведению. long long является самым большим поддерживаемым целочисленным типом со знаком, и мы используем фигурную инициализацию , чтобы предотвратить сужающие преобразования .

  • Наконец, мы std::forward вызываемый объект и std::invoke его с выбранным элементом. Спецификатор decltype(auto) обеспечивает сохранение типа и категории значения вызова.

Мы вызываем функцию с помощью std::array и лямбда-выражение , которое печатает выбранный элемент.

Начиная с C ++ 20, мы можем ограничить шаблон функции, используя понятия:

template <std::random_access_iterator RanIt,
          std::indirectly_­unary_­invocable<RanIt> F>
decltype(auto) select(RanIt begin, RanIt end, F&& f)
{
    // ...
}

До C ++ 20 мы также можем использовать SFINAE:

template <typename RanIt, typename F>
std::enable_if_t<
    std::is_base_of_v<
        std::random_access_iterator_tag,
        typename std::iterator_traits<RanIt>::iterator_category
    >,
    std::invoke_result_t<F, typename std::iterator_traits<RanIt>::value_type>
> select(RanIt begin, RanIt end, F&& f)
{
    // ...
}
1 голос
/ 18 марта 2020

Пара проблем с вашей программой:

  • У вас нет начального числа, поэтому цифры повторяются. Используйте
srand (time(NULL)); // #include <time.h>

перед использованием rand()

  • Ваши случайные числа не упорядочены, у вас есть 0-2, а затем 4, когда вы получаете 3 нет доступных опций. Если это специально, игнорируйте это замечание.

  • При rand() % 3 + 1; ваши случайные числа будут варьироваться от 1 до 3, поэтому opcao == 0 и opcao == 4 никогда не произойдут. Для интервала 0-4 вам понадобится что-то вроде:

 opcao = rand() % 5;
1 голос
/ 18 марта 2020

Я хотел бы добавить кое-что сюда, добавив к предыдущим ответам.

Прежде всего, вы не пишете код для себя . Как не являющийся носителем языка Engli sh, я понимаю, почему может показаться проще написать код на вашем родном языке, но не делайте этого!

Во-вторых, я внес изменения в код чтобы сделать его проще и удобочитаемее:

#include <time.h>
#include <cstdint>
#include <iostream>
#include <string>

constexpr uint32_t NUM_OF_POSSIBLE_PROMPTS = 4;

int main () {
    srand(time(NULL)); // seed according to current time

    for (uint32_t i = 0; i < 10; ++i)
    {
        int option = rand() % (NUM_OF_POSSIBLE_PROMPTS);

        std::string prompt = "";

        switch (option)
        {
            case 0:
                prompt = "option 0";
                break;
            case 1:
                prompt = "option 1";
                break;
            case 2:
                prompt = "option 2";
                break;
            case 3:
                prompt = "option 3";
                break;
            default:
                // some error handling!
                break;
        }

        std::cout << prompt << std::endl;
    }

    return 0;
}
  1. Я использую switch-case вместо if-else-if-else, что намного удобнее для чтения и эффективно !

  2. Я использую constexpr для хранения моего номера с жестким кодом - это плохая привычка иметь номера с жестким кодом в коде (в реальной программе я бы сделал значение constexpr также 10 для границ l oop).

  3. в c ++ (в отличие от c), мы используем std :: cout и его оператор << для печати, а не функцию printf. Это делает унифицированное поведение с другими типами потоков, такими как stringstream (что полезно при попытке построить строки в реальном времени, однако, это несколько тяжело для ресурсов!). </p>

Так как этот код более организован, для you легче понять, где может произойти возможная ошибка, а также уменьшается вероятность того, что это произойдет в первую очередь.

Например, использование флага -Wswitch-enum g cc гарантирует, что если вы используете enum, все значения должны обрабатываться в секциях switch-case (что, конечно, делает вашу программу менее подверженной ошибкам).

PS, Я добавил l oop только для того, чтобы показать вам, что этот код получает разные результаты каждый раунд, и вы можете проверить это, выполнив код несколько раз.

1 голос
/ 18 марта 2020

Вы не предоставили начальное число для генератора случайных чисел. Со страницы man,

Если значение начального числа не задано, функции автоматически высеиваются со значением 1.

Если у вас такое же начальное значение при каждом запуске вы всегда будете получать одну и ту же случайную последовательность.

...