c ++矢量随机洗牌部分
Posted
技术标签:
【中文标题】c ++矢量随机洗牌部分【英文标题】:c++ vector random shuffle part of it 【发布时间】:2009-11-03 14:23:24 【问题描述】:什么是随机播放向量中特定百分比元素的最佳方法。
假设我想要 10% 或 90% 的向量被洗牌。 不一定是前 10%,而是全面的 10%。
TIA
【问题讨论】:
您是否需要精确的概率属性?答案取决于它。 【参考方案1】:修改 Fisher-Yates shuffle 以对数组中 10% 的索引不执行任何操作。
这是我发布(来自 Wikipedia)并修改的 java 代码,但我认为您可以将其转换为 C++,因为这更多是算法问题而不是语言问题。
public static void shuffleNinetyPercent(int[] array)
Random rng = new Random(); // java.util.Random.
int n = array.length; // The number of items left to shuffle (loop invariant).
while (n > 1)
n--; // n is now the last pertinent index
if (rng.nextDouble() < 0.1) continue; //<-- ADD THIS LINE
int k = rng.nextInt(n + 1); // 0 <= k <= n.
// Simple swap of variables
int tmp = array[k];
array[k] = array[n];
array[n] = tmp;
【讨论】:
这并不能保证 10% 的数组会被洗牌,因为if (rng.nextDouble() < 0.1) continue
中的随机性
对。这让你大约 90% 的数组被洗牌。也许最好打乱所有数组索引的列表,然后如果当前索引在该打乱的索引数组的前 10% 中,则调用 continue。【参考方案2】:
你可以试试这个:
为向量的每个元素分配一个随机数。将随机数在您分配的随机数的最小 10% 中的元素打乱:您甚至可以想象用占位符替换向量中的 10%,然后根据它们的随机数对 10% 进行排序,然后将它们重新插入占位符所在的向量。
【讨论】:
【参考方案3】:如何编写自己的随机迭代器并使用 random_shuffle,如下所示:(完全未经测试,只是为了了解一下)
template<class T>
class myRandomIterator : public std::iterator<std::random_access_iterator_tag, T>
public:
myRandomIterator(std::vector<T>& vec, size_t pos = 0): myVec(vec), myIndex(0), myPos(pos)
srand(time(NULL));
bool operator==(const myRandomIterator& rhs) const
return myPos == rhs.myPos;
bool operator!=(const myRandomIterator& rhs) const
return ! (myPos == rhs.myPos);
bool operator<(const myRandomIterator& rhs) const
return myPos < rhs.myPos;
myRandomIterator& operator++()
++myPos;
return fill();
myRandomIterator& operator++(int)
++myPos;
return fill();
myRandomIterator& operator--()
--myPos;
return fill();
myRandomIterator& operator--(int)
--myPos;
return fill();
myRandomIterator& operator+(size_t n)
++myPos;
return fill();
myRandomIterator& operator-(size_t n)
--myPos;
return fill();
const T& operator*() const
return myVec[myIndex];
T& operator*()
return myVec[myIndex];
private:
myRandomIterator& fill()
myIndex = rand() % myVec.size();
return *this;
private:
size_t myIndex;
std::vector<T>& myVec;
size_t myPos;
;
int main()
std::vector<int> a;
for(int i = 0; i < 100; ++i)
a.push_back(i);
myRandomIterator<int> begin(a);
myRandomIterator<int> end(a, a.size() * 0.4);
std::random_shuffle(begin, end);
return 0;
【讨论】:
我认为最优雅的解决方案......但我肯定会在那里使用一些 Boost.Iterators 来减轻对所有样板代码的需求。【参考方案4】:一种方法可以使用 std::random_shuffle() ,通过控制输入范围来控制 % ....
【讨论】:
【参考方案5】:为什么不对随机选择的头寸进行 N 次互换,其中 N 由百分比决定?
因此,如果我有 100 个元素,则 10% 的随机播放将执行 10 次交换。每次交换随机选择数组中的两个元素并切换它们。
【讨论】:
这可能不会为您想要的所有各种洗牌提供统一的概率。 @kbloom:你所说的“你想要的各种洗牌”是什么意思?我们都知道洗牌是什么。什么是 10% 洗牌? 10% 的元素可能改变位置的洗牌?恰好有 10% 的元素改变了位置的洗牌?对大约 10% 的阵列进行完全洗牌,无论是预定义的还是随机挑选的?有很多这样的事情:给定一个圆中的随机弦,它比内接等边三角形的一条腿长的概率是多少?是二分之一、三分之一或四分之一。【参考方案6】:如果你有 SGI 的 std::random_sample
扩展,你可以这样做。如果没有,很容易在返回指定范围内均匀分布的随机整数的函数之上实现random_sample
(Knuth,第 2 卷,“算法 R”)。
#include <algorithm>
#include <vector>
using std::vector;
void shuffle_fraction(vector<int> &data, double fraction)
assert(fraction >= 0.0 && fraction <= 1.0);
// randomly choose the indices to be shuffled
vector<int> bag(data.size());
for(int i = 0; i < bag.size(); ++i) bag[i] = i;
vector<int> selected(static_cast<int>(data.size() * fraction));
std::random_sample(bag.begin(), bag.end(), selected.begin(), selected.end());
// take a copy of the values being shuffled
vector<int> old_value(selected.size());
for (int i = 0; i < selected.size(); ++i)
old_value[i] = data[selected[i]];
// choose a new order for the selected indices
vector<int> shuffled(selected);
std::random_shuffle(shuffled.begin(), shuffled.end());
// apply the shuffle to the data: each of the selected indices
// is replaced by the value for the corresponding shuffled indices
for (int i = 0; i < selected.size(); ++i)
data[selected[i]] = old_value[shuffled[i]];
不是最有效的,因为它使用三个“小”向量,但避免了调整 Fisher-Yates 算法以对向量的子集进行操作。在实践中,您可能希望这是一个在一对随机访问迭代器而不是向量上运行的函数模板。我没有这样做,因为我认为它会混淆代码,而你没有要求它。我也会采用大小而不是比例,让调用者决定如何舍入分数。
【讨论】:
【参考方案7】:您可以使用 shuffle bag 算法来选择 10% 的数组。然后对该选择使用正常的随机播放。
【讨论】:
以上是关于c ++矢量随机洗牌部分的主要内容,如果未能解决你的问题,请参考以下文章