【发布时间】:2015-05-22 12:21:30
【问题描述】:
在提出任何具体问题之前,请注意我的目标不是随机洗牌,而是执行完美的洗牌,就像理想的庄家对一组牌所做的那样,即将牌分成两半,然后进行一次洗牌(将半副牌中的一张牌与另一半副牌中的一张牌交错)。 (这实际上是 Sedgewick 编写的 C 第三版算法中的一个练习:nbr 11.3 第 445 页)
所以,我对 Fisher-Yates 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]);
}
洗牌操作的基本思想是跟踪数组'p'中应该插入下一个元素的位置,然后从位置'w'的数组数据中复制该元素留下一个空白空间,然后将数组从左向右移动以便将空白空间精确地移动到位置“p”,一旦完成,代码只需移动到 data[p] 之前保存的值“s”。很简单.. 它看起来可以正常工作(一些洗牌示例)
FROM: 12345678
TO: 15263748
FROM: IS THIS WORKING CORRECTLY OR NOT?
TO: ICSO RTRHEICST LWYO ROKRI NNGO T?
FROM: SHUFFLING TEST
TO: SNHGU FTFELSIT
我的问题在于 unshuffle 操作,我相信可以避免最后一个 for 循环中的交换序列,但我无法找到一种更有效的方法来做到这一点。问题是主循环 unshuffle 执行移位操作,最终使数组前半部分的元素以错误的顺序排列(因此,需要在第二个 for 中进行交换)。
我几乎可以肯定,必须有一种聪明的方法来完成这项工作,而无需如此复杂的代码..
您对 unshuffle 算法的改进有什么建议吗?或者对我所做的任何其他建议?
谢谢
【问题讨论】:
-
这不是
c,恕我直言。 -
我们称它为“Objectless c++”。说真的,我们不应该创建一个名为“Objectless C++”的标签吗?
-
我认为你做的很自然。这个效率更高但更复杂stackoverflow.com/questions/12338654/…
-
@user3528438 为什么称它为“无对象 C++”?您不仅有一个对象
vector<T>& data,而且还使用了模板。 -
最后一项可以洗牌吗?