Как получить два (псевдо) случайных, но отличных друг от друга контейнера итераторов / элементов?

У меня есть кусок кода, который генерирует мини-миссии для игроков.
Это просто, и чтобы получить две разные точки (начальную и конечную), у меня есть алгоритм, который выглядит следующим образом:

    std::vector<std::string> missions;

missions.push_back("Location_One");
missions.push_back("Location_Two");
missions.push_back("Location_Three");

//make sure our data has at least 2 elements (so we can actually pick two)
if(missions.size() > 1)
{
//Rand(inclusive min, exlusive max)
int mission_start_location = Rand(0,missions.size());
int mission_end_location = Rand(0,missions.size());

if(mission_start_location == mission_end_location)
{
//avoid possile infinite loop of calling "Rand" by Add/Decrement-if-equal algorithm
//basicly if mission_start_location == 0
if(!mission_start_location)
++mission_end_location;//or = 1, we have at least two elements so index 1 is valid
else
--mission_end_location;//so we won't got out of range
}
//do mission
}
else
{
//error
}

Это работает, однако мне было интересно, есть ли лучший способ достичь того, что я хочу, «путь C ++».

Мои вопросы:

  • Это лучший способ получить два разных значения из контейнера?
  • Как насчет контейнеров с нецелым индексом (например, std::map<std::string,std::string>)?
    • Как бы я получить два разных и случайных значений из этого?

Примечание: я хорошо осведомлен о do { } while(rand1 == rand2) метод. Я хочу избежать этого, потому что это Можно пойти в бесконечный цикл (зная, что мне повезет, это будет в производственном коде).

1

Решение

Да, вы можете иметь это по-другому.

  if(missions.size() > 1)
{
size_t const m_size = missions.size();
// get random number in the full range
int const m_start = Rand(0, m_size);
// get another number in a range reduced by 1
int m_end = Rand(0, m_size-1);
// if we are equal or above start we shift by 1 up
if (m_end >= m_start) ++m_end;
}
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Если ваш первый выбор выбрал 4, фактически удалите его, добавив второе случайное число в диапазоне-1.

| 0 | 1 | 2 | 3 | 4 (было 5) | 5 (было 6) | 6 (было 7) |

Чтобы отобразить второе случайное число обратно в исходную индексацию, просто добавьте 1, если вы находитесь выше первого случайного числа, поскольку эти индексы были сдвинуты на 1 из-за «удаления» первого индекса.


Таким образом, каждый индекс, который не выбран в первом Rand() вызов выбирается с той же вероятностью при втором вызове Rand(), (Если твой Rand() Реализация обеспечивает такое правильное распределение случайных чисел.)

3

Другие решения

Самое простое решение:

int mission_start_location = Rand(0,missions.size());
int mission_end_location;
do {
mission_end_location = Rand(0,missions.size());
} while (mission_end_location == mission_start_location);

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

Если вы действительно хотите сделать это с помощью двух вызовов вашего генератора случайных чисел:

int mission_start_location = Rand(0, missions.size());
int mission_end_location = Rand(0, missions.size() - 1);
if (mission_end_location >= mission_start_location) ++mission_end_location;

Я не знаю что Rand функции, которую вы используете, но вы можете посмотреть на функции, реализованные в <random>

1

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

    ...
int mission_start_location = Rand(0,missions.size());
int mission_end_location = Rand(0,missions.size());
if(mission_start_location == mission_end_location)
{
if(!mission_start_location)
++mission_end_location;
else
--mission_end_location;
}
...


Я предполагаю, что ваш Rand() функция использует первый аргумент в качестве нижнего включительно связаны и второй аргумент в качестве верхнего включительно граница возможного сгенерированного числа.

дела Rand(0,missions.size()); подвергнет вас риску чтение за концом вашего вектора. Помни что std::vectorиндексы основаны на 0, и когда Rand(0,missions.size()); случается, чтобы вернуть значение, равное missions.size(), тогда вы будете читать элемент один за концом вектора. Ты должен сделать

Rand(0,missions.size() - 1);
//                     ^^^ THIS

вместо.

Еще один на этой части вашего кода

    if(mission_start_location == mission_end_location)
{
if(!mission_start_location)
++mission_end_location;
else
--mission_end_location;
}

дела ++mission_end_location; а также --mission_end_location; Опять же, вы рискуете прочесть не только после конца вашего вектора, но и до его начального элемента.

Один правильный способ сделать то, что вы пытаетесь сделать, это

if(missions.size() > 1)
{
int mission_start_location;
int mission_end_location;

while(true) {
mission_start_location = Rand(0,missions.size());
mission_end_location = Rand(0,missions.size());

if(mission_start_location == mission_end_location) continue;
else break;
}

//do mission
}

Этот код повторяется до mission_start_location а также mission_end_location разные.

0

• Это лучший способ получить два разных значения из контейнера?

Это будет генерировать разные значения, но вы можете получить индекс, который выходит за пределы диапазона, потому что вы увеличиваете или уменьшаете. Я бы предложил просто заново сгенерировать одно из местоположений миссии, в то время как оно равно другому, а не увеличивать, поскольку это также устранит некоторую погрешность, вызывающую возрастающие причины. Кроме того, я не знаю, почему у вас есть if(!mission_start_location) блок, что ты пытаешься достичь с этим?

РЕДАКТИРОВАТЬ Я видел, что вы беспокоились о бесконечных петлях. Ты не должен быть. Это самый стандартный способ C ++ для генерации неравных случайных чисел, и ваши шансы попасть в бесконечный цикл очень маловероятно, даже если ваш вектор имеет размер 2 (в этом случае потребуется в среднем 2 итерации, чтобы гарантировать, что вы выбрали новое число).


• А как насчет контейнеров с нецелыми индексами (например, std :: map)?

• Как я могу получить два разных и случайных значения из него?

Большинство из них должны иметь begin() итератор. Вы можете использовать это, чтобы получить что-то случайное добавление случайного значения от 0 до size() увеличивая его в for цикл случайное количество раз (мое оригинальное решение не будет работать, потому что std::map begin() не позволяет + оператор, но позволяет ++ оператор) (см. этот для справки для карты. Вы также сможете найти другие контейнеры и здесь).

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector