我有一段代码可以为玩家制作迷你任务。 这很简单,并获得两个不同的点(开始和目的地)我有一个类似这样的算法:
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)
方法。我想避免这种情况,因为可以进入一个无限循环(知道我的运气会在生产代码中)。
答案 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()
之间添加一个随机值1>来随机增加一个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_location
和mission_end_location
不同。