Перетасовка объектов на форме

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
jonyroww
Сообщения: 2
Зарегистрирован: 28 май 2014, 16:26

Здравствуйте!
Пишу систему тестирования в QtCreator. Нужно чтобы при каждом запуске программы, три чекбокса меняли своё положение. Как можно это сделать?
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Не писал под QT, но почему-то уверен, что никаких проблем в том, чтобы "подвинуть" чекбокс на форме нету. Делаю вывод, что вызывает затруднение алгоритм изменения их положения. Как на счёт того, чтобы выяснить порядок чекбоксов, два раза вызывав rand()? Подробности нужны?
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

Бездумно вызывать rand() - нельзя. Надо еще следить, чтобы чекбоксы не пересекались.
It's a long way to the top if you wanna rock'n'roll
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Само собой не бездумно. Сначала rand()%3, потом rand()%2, причём с выравниванием. Но это уже подробности. Я же как раз спросил человека нужны ли подбробности или дальше уже сам догадается.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
jonyroww
Сообщения: 2
Зарегистрирован: 28 май 2014, 16:26

Romeo писал(а):Само собой не бездумно. Сначала rand()%3, потом rand()%2, причём с выравниванием. Но это уже подробности. Я же как раз спросил человека нужны ли подбробности или дальше уже сам догадается.
Поподробней пожалуйста)
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Хорошо, подробней, так подробней.

Сначала на пальцах, перенося задачу из программирования в реальный мир. Так понятнее суть уловить. Задача в том, чтобы N пронумерованных шариков, находящихся в мешке, расположить в случайном порядке. Как мы будет действовать?

- Вытаскиваем из мешка произвольный шарик из N шариков, кладём его на первое место.
- Вытаскиваем из мешка произвольный шарик из N-1 шариков, кладём его на второе место.
...
- Вытаскиваем из мешка один оставшийся шарик, кладём его на N-ное место.

Теперь вернёмся обратно в мир программрования.

У нас есть функция rand(), которая возвращает при каждом вызове "большое" случайное число. Если мы хотим получить случайное число от 0 до N-1, то нужно вычислить остаток от деления этого большого числа на N. В формуле это будет выгляеть rand() % N.

Что дальше? С программной точки зрения удобнее не "вытаскивать шарики из мешка", а на каждом шагу в этом же мешке их просто отодвигать в сторонку. К примеру, если у нас пронумерованные шарики - это вектор целых чисел, то в этом векторе будет граница перемешанных шариков: изначально она будет прижата к левой границе вектора и не будет содержать ни одного числа слева от себя, а во время работы алгоритма будет двигаться к концу вектора.

Ещё одно небольшое замечание. Благодаря тому, что шарики мы не вытаскиваем из мешка, а просто отодвигаем в сторону, последний шаг нам делать не нужно, так как один оставшийся шарик уже лежит на нужном месте.

От слов к делу:

Код: Выделить всё

#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>

void mix_vector(std::vector<int>& vec)
{
   if (vec.size() <= 1)
   {
      return; // вектор не нужно смешивать, если он пустой или содержит один элемент
   }

   srand(time(NULL)); // инициализация генератора случайных чисел

   int nBoundary = 0; // граница перемешанных шариков
   const int nRightBoundary = vec.size() - 1; // правая граница, до которой мы смешиваем.
   // Минус 1 потому, что последний шарик нет смысла выбирать, он и так остался один.

   while (nBoundary < nRightBoundary)
   {
      const int nNotMixedCount = vec.size() - nBoundary; // количество непемеремешанных шариков
      const int nSelectedNumber = rand() % nNotMixedCount; // номер шарика, который нужно отложить в сторону
      const int nSelectedIndex = nBoundary + nSelectedNumber; // индекс выбранного шарика в векторе
      std::swap(vec[nBoundary], vec[nSelectedIndex]); // отодвигаем выбранный шарик в сторонку
      ++nBoundary; // сдвигаем границу
   }
}

void print_vector(std::vector<int>& vec)
{
   std::vector<int>::const_iterator it = vec.begin();
   for (; it != vec.end(); ++it)
   {
      std::cout << *it << " ";
   }
   std::cout << std::endl;
}

int main()
{
   std::vector<int> vec;
   vec.push_back(0);
   vec.push_back(1);
   vec.push_back(2);

   print_vector(vec);
   mix_vector(vec);
   print_vector(vec);

   return 0;
}
Вывод программы:
0 1 2
2 0 1
Причём при повторных запусках, вторая строка меняется в произвольном порядке.

Специально для критиков напишу, что функция имплементирована намерянно в максимальном количестве строк. Всё ради того, чтобы можно было сделать коментарий для каждого выполняемого действия. Понятно, что функцию можно переписать чуть ли не в две строки, убрав временные переменные, и соорудив из них общую формулу. Так же можно заменить while на for. Помимо этого не исключено преобразование функции в темплейтную функцию, принимающую вектор с элементами любого типа, так как семантику типа элемента мы нигде не использовали. При желании топик стартер может провести эту работу сам.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Кому интересно, вот оптимизированный вариант функции:

Код: Выделить всё

template <class ElementType>
void mix_vector(std::vector<ElementType>& vec)
{
   if (vec.size() > 1)
   {
      srand(time(NULL));
      const int nRightBoundary = vec.size() - 1;
      for (int nBoundary = 0; nBoundary < nRightBoundary; ++nBoundary)
      {
         std::swap(vec[nBoundary],
                   vec[nBoundary + rand()%(vec.size() - nBoundary)]);
      }
   }
}
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Бездумно вызывать rand() - нельзя. Надо еще следить, чтобы чекбоксы не пересекались.
А как случайный порядок влияет на пересечения? Можно даже один раз вызвать rand() для генеринга индекса в массиве порядков, там всего то будет: 012, 021, 102, 120, 201 и 210, и того 6 вариантов. Для каждого варианта запомни заранее вычисленные координаты и присваивай их.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Абсолютно верно, если нам известно, что всегда будет ровно три варианта ответа, то можно выбрать одну из шести возможных комбинаций, один раз вызвав rand() % 6.

Моё решение хоть и более сложное, но зато более общее. Как я понимаю, в программе-тесте, которую пишет топикстартер, скорее всего количество возможных вариантов ответов на вопрос будет варьироваться и зависеть от самого вопроса. Иными словами оно не всегда будет 3. Так что generic решение больше подойдёт.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить