需要找出洗牌方法中的逻辑错误
我正在尝试编写一个方法,该方法接受一个整数数组(0-51,按顺序),将其切成两个单独的数组(下面函数中的 A 和 B 使用 cut 方法,我知道它确实有效),然后通过从 A 或 B 的底部随机选择 0、1 或 2 张牌并将它们添加到牌堆中,将两个阵列重新融合在一起。
(ps-“数组”我的意思是链接列表,我只是说数组是因为我认为它在概念上会更容易)
这是我到目前为止的代码,它有效,但是当涉及到卡片落地的位置时,存在明显的偏差。有人能发现我的逻辑错误吗?
[code]
void Deck::shuffle(){
IntList *A = new IntList();
IntList *B = new IntList();
cut(A, B);
IntListNode *aMarker = new IntListNode;
aMarker = A->getSentinel()->next;
//cout<< A->getSentinel()->prev->prev->data <<'\n'<<'\n';
IntListNode *bMarker = new IntListNode;
bMarker = B->getSentinel()->next;
//cout<< B->getSentinel()->prev->data;
deckList.clear();
srand(time(NULL));
int randNum = 0, numCards = 0, totalNumCards = 0;
bool selector = true, aisDone = false, bisDone = false;
while(totalNumCards < 52){
randNum = rand() % 3;
if(randNum == 0){
selector = !selector;
continue;
}
numCards = randNum;
if(!aisDone && !bisDone){
if(selector){
for(int i = 0; i < numCards; i++){
deckList.push_back(aMarker->data);
aMarker = (aMarker->next);
if(aMarker == A->getSentinel()){
aisDone = true;
break;
}
}
selector = false;
}else{
for(int i = 0; i < numCards; i++){
deckList.push_back(bMarker->data);
bMarker = (bMarker->next);
if(bMarker == B->getSentinel()){
bisDone = true;
break;
}
}
selector = true;
}
}
if(aisDone && !bisDone){
for(int i = 0; i < (52 - totalNumCards); i++){
deckList.push_back(bMarker->data);
bMarker = (bMarker->next);
if(bMarker == B->getSentinel()){
bisDone = true;
break;
}
}
//return;
}
if(bisDone && !aisDone){
for(int i = 0; i < (52 - totalNumCards); i++){
deckList.push_back(aMarker->data);
aMarker = (aMarker->next);
if(aMarker == A->getSentinel()){
aisDone = true;
break;
}
}
//return;
}
totalNumCards += numCards;
}
int tempSum = 0;
IntListNode *tempNode = deckList.head();
for(int j = 0; j < 52; j++){
//cout<< (tempNode->data) << '\n';
tempSum += (tempNode->data);
tempNode = (tempNode ->next);
}
if(tempSum != 1326)
system("PAUSE");
return;
}
[/code]
I'm trying to write a method that takes an array of integers (0-51, in that order), cuts it into two separate arrays (A and B in the below function by using the cut method, which I know for sure works) and then re-fuses the two arrays together by randomly selecting 0, 1 or 2 cards from the BOTTOM of either A or B and then adding them to the deck.
(ps- by "array" I mean linked list, I just said array because I thought it would be conceptually easier)
This is my code so far, it works, but there's a definite bias when it comes to where the cards land. Can anybody spot my logic error?
[code]
void Deck::shuffle(){
IntList *A = new IntList();
IntList *B = new IntList();
cut(A, B);
IntListNode *aMarker = new IntListNode;
aMarker = A->getSentinel()->next;
//cout<< A->getSentinel()->prev->prev->data <<'\n'<<'\n';
IntListNode *bMarker = new IntListNode;
bMarker = B->getSentinel()->next;
//cout<< B->getSentinel()->prev->data;
deckList.clear();
srand(time(NULL));
int randNum = 0, numCards = 0, totalNumCards = 0;
bool selector = true, aisDone = false, bisDone = false;
while(totalNumCards < 52){
randNum = rand() % 3;
if(randNum == 0){
selector = !selector;
continue;
}
numCards = randNum;
if(!aisDone && !bisDone){
if(selector){
for(int i = 0; i < numCards; i++){
deckList.push_back(aMarker->data);
aMarker = (aMarker->next);
if(aMarker == A->getSentinel()){
aisDone = true;
break;
}
}
selector = false;
}else{
for(int i = 0; i < numCards; i++){
deckList.push_back(bMarker->data);
bMarker = (bMarker->next);
if(bMarker == B->getSentinel()){
bisDone = true;
break;
}
}
selector = true;
}
}
if(aisDone && !bisDone){
for(int i = 0; i < (52 - totalNumCards); i++){
deckList.push_back(bMarker->data);
bMarker = (bMarker->next);
if(bMarker == B->getSentinel()){
bisDone = true;
break;
}
}
//return;
}
if(bisDone && !aisDone){
for(int i = 0; i < (52 - totalNumCards); i++){
deckList.push_back(aMarker->data);
aMarker = (aMarker->next);
if(aMarker == A->getSentinel()){
aisDone = true;
break;
}
}
//return;
}
totalNumCards += numCards;
}
int tempSum = 0;
IntListNode *tempNode = deckList.head();
for(int j = 0; j < 52; j++){
//cout<< (tempNode->data) << '\n';
tempSum += (tempNode->data);
tempNode = (tempNode ->next);
}
if(tempSum != 1326)
system("PAUSE");
return;
}
[/code]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
只使用 std::random_shuffle 怎么样?是的,它不适用于链表,但你可以将其更改为
vector
:)What about just using
std::random_shuffle
? Yeah, it won't work for linked list, but you can change it tovector
:)如果你的老师有道德教你如何编程,那么他们会鼓励你像这样用四行代码解决问题:
使用标准库是<正确的做事方式。当您可以使用标准库解决问题时,自己编写代码是错误的做事方式。你的老师没有教你正确的方法。如果他们想表达一个观点(例如,让你练习使用指针),那么他们应该更加注意选择他们给你的练习。
在演讲中,这里有一个比上面的解决方案更糟糕但比你的老师的更好的解决方案:
执行以下操作 52 次:
If your instructor would have the moral to teach you programming the way it should be done then they'd encourage you to solve the problem like so, with four lines of code:
Using the standard library is the right way of doing things. Writing code on your own when you can solve the problem with the standard library is the wrong way of doing things. Your instructor doesn't teach you right. If they want to get a point across (say, have you practice using pointers) then they should be more attentive in selecting the exercise they give you.
That speech given, here is a solution worse than the above but better than your instructor's:
52 times do the following:
对于大多数随机数生成器,低位是最少随机的位。所以你的路线
应进行修改,以便从
rand
的高位到中位获得更多值。您的期望可能会落空。我注意到,如果随机值为 0,则交换选择器。再加上
randNum
的相对非随机性,这可能是您的问题。也许您需要让事情变得不那么随机,以使它们看起来更随机,例如每次循环时交换选择器,并始终从所选牌组中取出一张或多张牌。For most random number generators, the low bits are the least random ones. So your line
should be modified to get its value more from the high- to middle-order bits from
rand
.Your expectations may be off. I notice that you swap the selector if your random value is 0. Coupled with the relative non-randomness of
randNum
, this may be your problem. Perhaps you need to make things less random to make them appear more random, such as swapping the selector every time through the loop, and always taking 1 or more cards from the selected deck.注释:
这应该在应用程序运行期间只调用一次。通常最好在开始时在 main() 中调用它。
每行一个标识符。每个编写的编码标准都有这条规则。它还可以防止使用指针时可能出现的一些细微错误。习惯它。
rand 的底部位是最不随机的。
问:
剩下的太复杂了,看不懂。
我可以想到更简单的技术来很好地洗牌一副牌:
上面模拟从牌组 A 中随机挑选一张牌并将其放在牌组 B 的顶部(牌组 B 最初是空的)。当循环完成时,B 现在是 A(因为它已就地完成)。
因此,每张牌(来自 A 的牌)被放置在 B 中任何位置的概率都是相同的。
Comments:
This should only be called once during an applications run. This it is usally best to call it in main() as you start.
One identifier per line. Every coding standard written has this rule. It also prevents some subtle errors that can creep in when using pointers. Get used to it.
The bottom bits of rand are the lest random.
Question:
The rest is too complicated and I don't understand.
I can think of simpler techniques to get a good shuffle of a deck of cards:
The above simulates picking a card at random from the deck A and putting it on the top of deck B (deck B initially being empty). When the loop completes B is now A (as it was done in place).
Thus each card (from A) has the same probability of being placed at any position in B.