C++ Размещение случайных значений в диаграмме умножения (вложенный цикл)

Я создал таблицу умножения целых чисел 1...10. Вот код:

for (int i = 1; i <= 10; i++) {
    for (int j = 1; j <= 10; j++) {
      std::cout << "\t" << i * j;
    }
    std::cout << std::endl;
}

И мой вывод таков:

1   2   3   4   5   6   7   8   9   10
2   4   6   8   10  12  14  16  18  20
3   6   9   12  15  18  21  24  27  30
4   8   12  16  20  24  28  32  36  40
5   10  15  20  25  30  35  40  45  50
6   12  18  24  30  36  42  48  54  60
7   14  21  28  35  42  49  56  63  70
8   16  24  32  40  48  56  64  72  80
9   18  27  36  45  54  63  72  81  90
10  20  30  40  50  60  70  80  90  100

Проблема, с которой я борюсь, заключается в том, что мне нужно вставить случайное число, представляющее значение ошибки, в таблицу умножения. Например:

1   2   3   4   5   6   7   8   9   10
2   4   6   8   10  12  14  16  18  20
3   6   9   13  15  18  21  24  27  30

Как вы можете видеть в этом примере, значение ошибки равно 13, где 3 x 4 = 12, но вместо этого стоит число 13.

Я просмотрел бесчисленное количество сообщений на StackOverFlow и в Интернете.

Следует ли мне использовать другой цикл для вставки случайного числа? Если да, то как это сделать?

4 ответа

#include <iostream> // input/output
#include <random>

struct FieldData {
    using Row = unsigned int;
    using Col = unsigned int;

    const Row rows{10};
    const Col cols{10};    

    Row err_row;
    Col err_col;

    unsigned int err_value;

    FieldData() {} // use default values for rows/cols
    FieldData(Row max_row, Col max_col) : rows(std::move(max_row)), cols(std::move(max_col)) {} // use given values for rows/cols
};

void print_field(const FieldData& data)
{
    for (FieldData::Row row=1;row <= data.rows; row++)
    {
        for (FieldData::Col col = 1; col <= data.cols; col++)
        {
            if (row == data.err_row && col == data.err_col)
                std::cout << "\t" << data.err_value;
            else
                std::cout << "\t" << row*col;
        }
        std::cout << std::endl;
    }
}

FieldData get_field(std::mt19937& generator)
{
    FieldData data;
    std::uniform_int_distribution<FieldData::Row> dist_rows(1, data.rows); // random for row
    std::uniform_int_distribution<FieldData::Row> dist_cols(1, data.cols); // random for col
    std::uniform_int_distribution<FieldData::Row> dist_result(1, data.rows*data.cols); // random for result


    data.err_row = dist_rows(generator);
    data.err_col = dist_cols(generator);

    data.err_value = dist_result(generator);

    if (data.err_value != data.err_row*data.err_col)
        data.err_value--; // avoid correct value on error pos

    return data;
}

int main()
{
    std::random_device rd;
    std::mt19937 generator(rd());

    while (true)
    {
        auto field = get_field(generator);
        print_field(field);


        // some code to ask the user and so on.... (std::cin ...)


        std::cout << "Error value '" << field.err_value << "' is on row " << field.err_row << ", col " << field.err_col << std::endl;

        // if (... some code to check if user wants to break...)
        break;

        // code to go on
        // go on
        std::cout << std::endl;
    }
}

По просьбе автора, версия без дополнительных конструкций:

#include <iostream> // input/output
#include <random>

int main()
{
   std::random_device rd;
   std::mt19937 generator(rd());

   unsigned int rows = 10;
   unsigned int cols = 10;
   std::uniform_int_distribution<unsigned int> dist_rows(1, rows); // random for row (1 to value of field_rows)
   std::uniform_int_distribution<unsigned int> dist_cols(1, cols); // random for col (1 to value of field_cols)
   std::uniform_int_distribution<unsigned int> dist_result(1, rows * cols); // random for result

   while (true) // infinite loop - must be interrupted explicitly with break or similar
   {    
      unsigned int err_row = dist_rows(generator);     // Generate the random number that defines the row
      unsigned int err_col = dist_cols(generator);     // Generate the random number that defines the col
      unsigned int err_value = dist_result(generator); // Generate the random value

      if (err_value != err_row * err_col)
         err_value--; // avoid correct value on error position - simple decrement the value

      // print_field
      for (unsigned int row = 1; row <= rows; row++)
      {
         for (unsigned int col = 1; col <= cols; col++)
         {
            if (row == err_row && col == err_col) // condition to insert the error value
               std::cout << "\t" << err_value;
            else
               std::cout << "\t" << row * col;
         }
         std::cout << std::endl;
      }

      //
      // some code to ask the user and so on.... (std::cin ...)
      //

      std::cout << "Error value '" << err_value << "' is on row " << err_row << ", col " << err_col << std::endl;

      // if (... some code to check if user wants to break...)
      break;

      // code to go on
      // go on
      std::cout << std::endl;
   }
}

Надеюсь, этот ответ поможет вам, я немного разделил разные части, чтобы сделать его более понятным для вас. Код должен хотя бы дать вам общее представление о том, как можно реализовать нечто подобное. В зависимости от варианта использования, конечно, будет уместен отдельный класс или что-то подобное. Код во втором варианте отличается от функциональности только тем, что uniform_int_distributions находятся за пределами области while(true) и, таким образом, не всегда "перестраиваются", как в случае первого варианта (поскольку более динамичный и модульный подход был бы возможен в принцип).

Вы можете использовать <random> заголовочный файл для этого.

std::random_device rd;
std::mt19937 gen(rd());
// it produces uniformly distributed random integers in the range [a, b] (both inclusive)
std::uniform_int_distribution<> dis(0, 1); // either add 0 or 1 (1 will be the error -- can change a,b according to your need)

for (int i = 1; i <= 10; i++)
{
  for (int j = 1; j <= 10; j++)
  {
    std::cout << "\t" << i * j + dis(gen); // just add the error value to your multiplication
  }
  std::cout << std::endl;
}

Следует ли использовать другой цикл для вставки случайного числа? Если да, то как это сделать?

Вы вставляете только одну ошибку, поэтому цикл для этого не имеет особого смысла.

Хорошо, а почему бы вам сначала не решить, куда идет случайное число? Как только вы это узнаете, вы можете изменить тело внутреннего цикла следующим образом:

int value = i * j;
if ((i == error_location.row) and (j = error_location.column)) {
    value = // code for introducing an error, e.g. sample a random answer,
            // or a random offset added to the correct answer etc.
}
std::cout << "\t" << i * j;

Решение о позиции для ошибочных значений может быть принято с использованием заголовка C++, как объясняется в этом вопросе. В вашем случае это будет примерно так:

constexpr const int table_leg { 10 };
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> coordinate_distribution(0, table_leg - 1);
struct { int row, col; } error_location = 
    { coordinate_distribution(gen), coordinate_distribution(gen) };

Примечания:

  • Предлагаю заменить i с участием row или row_index а также j с участием col или column или column_index; также, используя именованную константу вместо повторения "магического числа"10.
  • Я предположил, что вы не хотите хранить свою таблицу умножения в буфере, а затем заменить одно значение там ошибкой. Если будут хранить таблицы где - то, вы могли бы избежать сложного внутреннего цикла и просто установить ошибку после заполнения всех правильных значений.

Так что спасибо всем, особенно @Wolfgang. Я прочитал случайный заголовочный файл C++11 и его функции. Я создал исходный код со случайными функциями и без них. Мы будем признательны за любой канал о том, как сделать мой исходный код более эффективным. Помимо этого, он достигает того, что мне нужно.

#include <iostream>
#include <ctime>
#include <iomanip>

const unsigned int ROWS = 10;
const unsigned int COLUMNS = 10;

void randomValue(unsigned int &, unsigned int &, unsigned int &);

int main() 
{   
  unsigned int rValue;
  unsigned int cValue;
  unsigned int pValue;

  unsigned int rowError; 
  unsigned int columnError;
  unsigned int productError;

  unsigned int userRowGuess;
  unsigned int userColumnGuess;

  char choice;  

  do 
  { 
    randomValue(rValue, cValue, pValue); 

    std::cout << "\033[2J\033[0;0H"; // This code clears the screen 
    std::cout << std::endl << std::endl;       
    std::cout << std::setw(35) << "++++++++++++++++++++++++" << std::endl;
    std::cout << std::setw(35) << "+ Multiplication Table +" << std::endl;
    std::cout << std::setw(35) << "++++++++++++++++++++++++" << std::endl;
    std::cout << std::endl << std::endl;   

    while (true)
    { 
      rowError = rValue;
      columnError = cValue;
      productError = pValue;

      if (productError == rowError * columnError)
      {
        productError--;
      }

      for (unsigned int row = 1; row <= ROWS; row++)
      {
        for (unsigned int column = 1; column <= COLUMNS; column++)
        {
          if (row == rowError && column == columnError)
          { 
            std::cout << "\t" << "" << productError;       
          }
          else
          {
            std::cout << "\t" <<  row * column;
          }
        }
      std::cout << std::endl;
      }  
      break;
    }


    std::cout << std::endl << std::endl;
    std::cout << "\t\t" << "Type in the column number " << std::endl;
    std::cout << "\t\t" << "of the location of the    " << std::endl;
    std::cout << "\t\t" << "error & then press        " << std::endl;
    std::cout << "\t\t" << "[Enter]: ";
    std::cin >> userColumnGuess;
    std::cout << std::endl;

    std::cout << "\t\t" << "Type in the row number " << std::endl;
    std::cout << "\t\t" << "of the location of the    " << std::endl;
    std::cout << "\t\t" << "error & then press        " << std::endl;
    std::cout << "\t\t" << "[Enter]: ";
    std::cin >> userRowGuess;
    std::cout << std::endl;

    if (userRowGuess != rowError && userColumnGuess != columnError)
    {
      std::cout << "\t\t" << "Your answer was incorrect!" << std::endl << std::endl;
      std::cout << "\t\t" << "Error value '" << productError << "' is located" << std::endl;
      std::cout << "\t\t" << "on row " << rowError << ", column " << columnError << "." << std::endl;
    }
    else
    {
      std::cout << "\t\t" << "You are correct! You win!" << std::endl;
    }


    std::cout << std::endl;
    std::cout << "\t\t" << "Would you like to play again?" << std::endl << std::endl;
    std::cout << "\t\t" << "Type in 'Y' for yes or 'N'" << std::endl;
    std::cout << "\t\t" << "for no & then press [Enter]: ";
    std::cin >> choice;

    while (choice != 'y' && choice != 'Y' && choice != 'n' && choice != 'N')
    {
      std::cout << std::endl;
      std::cout << "\t\t" << "Invalid entry. Only 'Y' or 'N'" << std::endl;
      std::cout << "\t\t" << "are accepted answers." << std::endl << std::endl;
      std::cout << "\t\t" << "Would you like to play again?" << std::endl << std::endl;
      std::cout << "\t\t" << "Type in 'Y' for yes or 'N' for" << std::endl;
      std::cout << "\t\t" << "no & then press [Enter]: ";
      std::cin >> choice;
    }   
    std::cout << std::endl;

  } while (choice == 'y' || choice == 'Y'); 

  std::cout << "\t\t" << "Press [Enter] to continue....." << std::endl;
  std::cin.get();
  std::cin.get();

  return 0;
}

void randomValue(unsigned int &rValue, unsigned int &cValue, unsigned int &pValue)
{ 
  srand((unsigned)time(NULL));

  unsigned int r = rValue = (rand() % ROWS) + 1;
  unsigned int c = cValue = (rand() % COLUMNS) + 1;
  unsigned int p = pValue = (rand() % (ROWS * COLUMNS)) + 1;
}
Другие вопросы по тегам