1

我有一段代码可以为玩家生成小任务。这很简单,要获得两个不同的点(起点和终点),我有一个如下所示的算法:

    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)方法。我想避免这种情况,因为它可以进入无限循环(知道我的运气会在生产代码中)。

4

4 回答 4

3

是的,你可以有另一种方式。

  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,则通过在 range-1 上设置第二个随机数来虚拟删除它。

| 0 | 1 | 2 | 3 | 4(原为 5)| 5(原为 6)| 6(原为 7)|

要将第二个随机数映射回原始索引,如果您高于第一个随机数,只需添加 1,因为这些索引由于“删除”第一个索引而被移动了 1。


这样,在第一次Rand()调用中未选择的每个索引在第二次调用时以相同的概率被选中Rand()。(如果您的Rand()实现提供了这样正确的随机数分布。)

于 2013-07-15T02:59:17.020 回答
1

最简单的解决方案是:

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++ 方法可以做到这一点。您可以使用标准库函数shuffle随机打乱任务容器,但这会产生随机打乱容器的副作用,并且仅获取两个随机元素就过分了。

如果您真的想通过对随机数生成器的两次调用来做到这一点:

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>

于 2013-07-15T02:43:10.883 回答
0

•这是从容器中获取两个不同值的最佳方式吗?

这将生成不同的值,但您可能会因为您递增和递减而获得超出范围的索引。我建议只重新生成一个任务位置,而它等于另一个而不是递增,因为这也将消除递增导致的一些偏差。另外,我不知道你为什么有if(!mission_start_location)障碍,你想用这个来达到什么目的?

编辑我看到你担心无限循环。你不应该。这是生成不相等随机数的最标准 C++ 方法,即使您的向量大小为 2,您进入无限循环的机会也极小(在这种情况下,平均需要 2 次迭代才能保证您选了一个新号码)。


• 非整数索引容器(例如std::map)呢?

•如何从中获得两个不同的随机值?

其中大多数应该有一个begin()迭代器。您可以通过在 0 之间添加一个随机值并size()在循环中将其递增for随机次数来使用它来获得随机值(我的原始解决方案不起作用,因为std::map begin()不允许+运算符,但确实允许++运算符)(请参阅this for地图参考。您也可以在这里找到其他容器)。

于 2013-07-15T02:33:43.737 回答
0

首先,您现在正在做的是使用 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_locationmission_end_location不同。

于 2013-07-15T02:40:08.207 回答