如何获得两个(伪)随机但彼此不同的容器迭代器/元素?

时间:2013-07-15 02:22:20

标签: c++ c++11 random containers elements

我有一段代码可以为玩家制作迷你任务。 这很简单,并获得两个不同的点(开始和目的地)我有一个类似这样的算法:

    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 个答案:

答案 0 :(得分: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,则通过在范围-1上使用第二个随机数来实际删除它。

|  0  |  1  |  2  |  3  |  4(was 5) |  5(was 6)  |  6(was 7)  |

要将第二个随机数映射回原始索引,只需在第一个随机数之上加1,因为这些索引由于第一个索引的“删除”而移位了1。

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

答案 1 :(得分: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>

中实现的函数

答案 2 :(得分:0)

  

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

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

编辑我看到你担心无限循环。你不应该。这是产生非相等随机数的最标准的C ++方式,即使你的向量大小为2,你进入无限循环的可能性也不太可能(在这种情况下,它会占用平均2次迭代,以保证您选择了一个新的数字。)


  

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

     

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

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

答案 3 :(得分: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不同。