【问题标题】:C++ - Randomly select strings without selecting it more than onceC ++ - 随机选择字符串而不多次选择它
【发布时间】:2013-10-28 18:25:15
【问题描述】:

我的手上装满了物品,每个物品都包含几个字符串。现在它被设置为结构,每个结构都包含一个带有键 1...n 的映射,每个字符串一个 (map<int,string> strs),如果存在更好的方法,可以更改它。我需要在不重叠的情况下随机访问所有这些字符串,并且知道我已经完成了。我该如何做到这一点,无论是使用地图还是其他数据结构?谢谢。

【问题讨论】:

  • 您需要按随机顺序枚举这些字符串多少次?一次?很多次?
  • 是否所有的字符串都是唯一的,或者每个对象中的映射是否可以包含也在另一个对象的映射中的字符串?
  • 我认为您正在寻找“洗牌”。样本很多(通常搜索“洗牌套牌+语言”会得到很好的结果)
  • 为什么不使用std::vector 而不是std::map?如果只是因为您想要基于 1 的索引而不是基于 0 的索引,那么以下两种方法都会更有效:(1) 忽略元素 0 的存在,或 (2) 将 1 添加到索引中。

标签: c++ string random map


【解决方案1】:

这是Fisher-Yates shuffle 的一些代码:

template <class T>
std::vector<T> shuffle(std::vector<T> &vect)
{
    std::vector<T> shuffled = vect;
    for(int i = shuffled.size()-1; i >= 1; i--) {
        int idx = rand() % (i+1);
        T tmp = shuffled[idx];
        shuffled[idx] = shuffled[i];
        shuffled[i] = tmp;
    }
    return shuffled;
}

这将接受一个向量,并以随机顺序返回它的副本。如果你有一个字符串向量,你可以这样使用(我这里用的是c++11):

int main()
{
    srand(time(NULL));
    std::vector<std::string> strs = {"foo", "bar", "baz", "stack", "overflow"};
    for(auto &str : shuffle(strs)) {
        std::cout << str << std::endl;
    }
    return 0;
}

当然,如果你像我一样懒惰,&lt;algorithm&gt; 中总会有 random_shuffle() 函数:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

int main()
{
    std::vector<std::string> strs = {"foo", "bar", "baz", "stack", "overflow"};
    std::random_device rd;
    std::mt19937 g(rd()); // Use a good random number generaor
    std::random_shuffle(strs.begin(), strs.end(), g); // this does the shuffle
    for(auto &str : strs) {
        std::cout << str << std::endl;
    }
    return 0;
}

希望这会有所帮助!

【讨论】:

  • 如果您想使用指定更健壮的 rng 同时仍使用标准库进行混洗,请使用 std::random_device 启动其中一个罐装生成器(例如 std::mt19937),然后使用 @ 987654324@,使用你的 rng 作为生成器参数。
  • ^ 确实!我会加进去的。:)
【解决方案2】:

一个可怕的解决方案,不要这样做。对于大型候选向量非常慢,这具有 n 平方复杂度。洗牌更好,它具有线性复杂度。

std::vector<int> RandomThing(int number, int min, int max)
{
    assert(!"RandomThing" && min < max);
    std::vector<int> candidates;
    for(int i=min; i<max; i++)
        candidates.push_back(i);

    std::vector<int> result;
    for(int i=0; i<number;)
    {
        int candidate_index = rand() % candidates.size();
        result.push_back(candidates[candidate_index]);

        std::vector<int>::iterator it = candidates.begin();
        std::advance(it, candidate_index);
        candidates.erase(it);
    }
    return result;
}

【讨论】:

  • 你为什么不直接洗牌候选人向量?此外,将 mod 与 rand() 一起使用也很糟糕。
  • 确实如此,只需一次性完成所有内容,而不必从向量中删除内容(将算法转换为 n 平方),速度要快得多。对于那个很抱歉。将解决方案留在这里,以便人们可以看到这样做的坏方法,但重写文本以反映它的坏处。同样关于 rand() % X,唯一的问题是“是否足够好”。当然它有明显的趋势,但如果这不是问题,那么我认为没有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 2013-02-21
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-07
相关资源
最近更新 更多