在提出任何具体问题之前,请注意我的目标不是随意地随机播放阵列,而是要进行完美的洗牌,因为理想的经销商可以使用一套牌,即将牌组分成两半并且执行一个洗牌通道(从一个半牌中插入一张牌,另一半牌中插入一张牌)。 (这实际上是Sedgewick的C第三版Algorithms的一个练习:nbr 11.3第445页)
所以,我对Fisher-Yates shuffle等算法并不感兴趣。
说,我的观点是在执行shuffle时避免使用任何辅助数组,我能够提供的代码如下:
template<typename T>
void two_way_shuffle(vector<T>& data,int l,int r)
{
int n=(r-l)+1;
int m=(r+l)/2;
if(n%2==0) ++m;
int s,p{l+1};
for(;p<=r;p+=2,m++)
{
s=data[m]; //Make space
//Shift the elements
for(int i{m};i>p;i--)
data[i]=data[i-1];
//Put the element in the final position
data[p]=s;
}
}
template<typename T>
void two_way_unshuffle(vector<T>& data,int l,int r)
{
int n=(r-l)+1;
if(n%2!=0){
--r;
--n;
}
int m=(r+l)/2;
int s,p{l+1},w{r-1};
for(;p<=w;p++,w--)
{
s=data[w];
for(int i{w};i>p;i--)
data[i]=data[i-1];
data[p]=s;
}
//Complete the operation
for(p=l+1,w=m;p<w;p++,w--)
swap(data[p],data[w]);
}
洗牌操作旁边的基本思想是跟踪阵列中的位置&#39; p&#39;应该插入下一个元素的位置,然后从位置&#39; w&#39;的数组数据中复制该元素。留下一个空的空间,然后将数组从左向右移动,以便将空的空间精确地移动到位置,一旦完成,代码只需移动到数据[p]先前保存的值&#39; S&#39 ;.非常简单..它看起来工作正常(一些随机的例子)
FROM: 12345678
TO: 15263748
FROM: IS THIS WORKING CORRECTLY OR NOT?
TO: ICSO RTRHEICST LWYO ROKRI NNGO T?
FROM: SHUFFLING TEST
TO: SNHGU FTFELSIT
我的问题是在非洗牌操作中,我相信最后一个for循环中的交换序列可以避免,但是我无法找到切割器的方法来做到这一点。问题是主循环非洗牌执行移位操作,最终使数组的前半部分的元素以错误的顺序排列(因此,需要在第二个中进行交换)。
我几乎可以肯定,如果没有这样的代码复杂功能,必须有一种聪明的方法来完成这项工作。
您对非洗牌算法可以改进的内容有什么建议吗?或者关于我做过什么的任何其他建议?
由于
答案 0 :(得分:0)
我认为您的代码保留了太多变量。当您对阵列进行随机播放时,您将处理大小逐渐减小的范围[lo, hi]
,您可以将最右边的条目hi
与元素块交换到左侧[lo, hi)
。您可以根据这两个变量来表达算法:
0 1 2 3 4 5
------- swap [1, 3) and [3]
0 3 1 2 4 5
---- swap [3, 4) and [4]
0 3 1 4 2 5
- [5, 5) is empty: break
对于奇数个元素,这是相同的,除了空范围越界,这是可以的,因为我们不访问它。这里的操作是:向右移动块,将旧hi
元素放在lo
位置,将lo
和hi
放在步幅2和1中。
当你取消洗牌时,你必须恢复以下步骤:在最后一个元素处开始lo
和hi
,对于奇数大小的数组,在偶数大小和一个元素之外开始。然后按相反顺序执行所有步骤:首先减少lo
和hi
,然后向左移动块并将旧lo
放在hi
位置。当lo
达到1时停止。(它将达到1,因为我们从奇数指数开始,我们减少2。)
您可以通过打印lo
和hi
来测试您的算法:它们必须相同才能进行改组和非洗牌,只能按相反的顺序进行。
所以:
template<typename T>
void weave(std::vector<T>& data)
{
size_t lo = 1;
size_t hi = (data.size() + 1) / 2;
while (lo < hi) {
T m = data[hi];
for (size_t i = hi; i-- > lo; ) data[i + 1] = data[i];
data[lo] = m;
lo += 2;
hi++;
}
}
template<typename T>
void unweave(std::vector<T>& data)
{
size_t n = data.size();
size_t lo = n + n % 2 - 1;
size_t hi = lo;
while (hi--, lo-- && lo--) {
T m = data[lo];
for (size_t i = lo; i < hi; i++) data[i] = data[i + 1];
data[hi] = m;
}
}
我删除了左右索引,这使得代码不那么灵活,但希望阅读更清晰。您可以将它们重新放入,只需要它们来计算lo
和hi
的初始值。