如何在不存储卡片的情况下实施经销商类别?
-
题
即使只有52张牌,我在“ 解释”部分描述的
permutationIndex
也是一个巨大的数字; 它是52!
一个数字52!
,需要29个字节来存储。因此, 我不知道计算大范围的
permutationIndex
的简单方法 ,并以最小成本存储索引,或者也可以计算它。 我在想这个问题的解决方案是三种算法:-
一种算法,它计算正确的
permutationIndex
来实现Dealing
方法 -
一种计算正确
permutationIndex
以实现Collect
方法的算法 -
一种以最小成本存储(或计算)
permutationIndex
的算法
-
-
说明
我最初尝试使用置换实现一个范围从
int.MinVale
到int.MaxValue
的整数句柄生成器 。因为范围非常大,所以我从实现一个
Dealer
类开始, 有52张卡,它们并不真正存储像hashset或array这样的卡片组,甚至不需要随机 (初始除外)。对于给定范围的序数,我认为其中一个完整排列的每个序列都有一个索引,并将其命名为
permutationIndex
。 我使用索引来记住它是哪个排列而不是真正存储序列。 序列是卡片组的可能顺序之一。这里有一个动画图形模拟示例,以显示我的想法。
每次我发卡时,我都会更改
permutationIndex
并dealt
(已发卡的数量),我知道哪些卡是那些卡,哪些卡还在手中。 当我收回已发卡时,我会知道卡号,并将其放在顶部,它也会成为下次交易的卡。 在动画中,colleted
是卡号 。
有关更多信息,请按以下方式
-
代码说明
仅有三个3的概念样本
Dealer
类如下。 代码是用c#编写的,我也在考虑任何与语言无关的解决方案。以下是示例代码的一些描述
-
使用
Dealing()
方法,我们得到一些处理的卡片。 它总是返回最右边的数字(与数组相关),然后通过更改permutationIndex
将其左边的数字(比如下一个可用的数字)滚动到最右边的位置。 -
方法
Collect(int)
用于收集并将处理后的卡片放回到牌组中。 它会改变permutationIndex
,根据卡的数量返回给经销商。 -
dealt
的整数表示我们dealt
了多少张牌; 从最左边到存储在dealt
中的计数都是发牌。 使用permutationIndex
,我们知道卡的顺序。 -
不使用示例代码中的
int[,]
数组,只是为了帮助设想排列。 switch语句被认为是用计算permutationIndex
算法实现的。 -
permutationIndex
与此答案中描述的内容相同
快速置换 – >数字 – >置换映射算法
-
-
示例代码
public static class Dealer { public static void Collect(int number) { if(1>dealt) throw new IndexOutOfRangeException(); switch(permutationIndex) { case 5: case 0: switch(number) { case 3: break; case 2: permutationIndex=1; break; case 1: permutationIndex=4; break; } break; case 4: case 3: switch(number) { case 3: permutationIndex=5; break; case 2: permutationIndex=2; break; case 1: break; } break; case 2: case 1: switch(number) { case 3: permutationIndex=0; break; case 2: break; case 1: permutationIndex=3; break; } break; } --dealt; } public static int Dealing() { if(dealt>2) throw new IndexOutOfRangeException(); var number=0; switch(permutationIndex) { case 5: permutationIndex=3; number=3; break; case 4: permutationIndex=0; number=1; break; case 3: permutationIndex=1; number=1; break; case 2: permutationIndex=4; number=2; break; case 1: permutationIndex=5; number=2; break; case 0: permutationIndex=2; number=3; break; } ++dealt; return number; } static int[,] sample= new[,] { { 1, 2, 3 }, // 0 { 1, 3, 2 }, // 1 { 3, 1, 2 }, // 2 { 3, 2, 1 }, // 3 { 2, 3, 1 }, // 4 { 2, 1, 3 }, // 5 }; static int permutationIndex; static int dealt; }
这不完全是你想要在这里完成的,但如果你想从一副牌的随机排序处理,你使用一个shuffle算法。 典型的混洗算法是Fisher-Yates 。 shuffle算法将创建一个以随机顺序列出卡号的数组(13,5,7,18,22,…等)。 处理你从数组中的第一个元素开始并继续前进。
我也在努力查看整个图片,但你可以将每个排列转换为base(52),其中一个字符代表每张卡片,并且有一个代表每个排列的字符串。
所以黑桃可能是1-9 (ace - 9)
, 0ABC (10, JQK)
,然后是DEFG
……开始心灵等等。
所以一张3张牌,2张Spade(2),3张Heart(F)和2张Diamond(比如说e),会有这些排列数字:
2Fe 2eF F2e Fe2 eF2 e2F
您可以通过将基数52转换为基数为10的转换来来回转换为int / long / bigint。
这是对基数之间转换的介绍 。
所以e2F将是F + 2*52 + e * 52^2
,这将是16 + 2*52 + 43*52*52 = 116392
所以116392将是你的排列数。
(顺便说一下。我猜它是2颗钻石是’e’和43,你可以数一数,看看它究竟是什么样的)
解决此问题的一种方法是使用(伪)随机数生成器(如Mersenne Twister ),然后仅存储每笔交易的种子编号。 由于每次从同一种子获得相同的随机数序列,它用于表示整个交易(使用从该种子生成的随机数来驱动所发出的卡)。
[编辑…]
该交易的一些伪代码:
while (#cards < cardsNeed) card = getCard(random()) if (alreadyHaveThisCard(card)) continue [do something with the card...]
如果我理解正确,以下代码实现了这一点:
public class Dealer { public int Dealing() { var number= _freeCards.Count>0 ?_freeCards.Dequeue() :_lastNumber++; _dealtCards.Add(number); return number; } public void Collect(int number) { if(!_dealtCards.Remove(number)) throw new ArgumentException("Card is not in use", "number"); _freeCards.Enqueue(number); } readonly HashSet _dealtCards=new HashSet (); readonly Queue _freeCards=new Queue (); // "Pool" of free cards. int _lastNumber; }
虽然我在理解你在这里想要实现的内容时遇到了一些问题,但我认为互质会产生一堆排列数; 那就是:如果你不太关心分配。 你可以使用Euclidian算法 。
代数(集合论)表明你可以简单地使用x =(x + coprime)%set.Length来获取集合中的所有元素。 我想每个互质都是你描述的排列数。
那就是说我不确定使用生成的互质作为“随机数发生器”时你得到的分布是什么; 我想某些分布将比其他分布更频繁地发生,并且许多分布将从生成的数字中排除,原因很简单,因为生成器将在环中选择数字。 我在这里有点创意,所以也许它符合你的需求,虽然它可能不是你想要的答案。
我真的不明白你的问题,但我这样解释:你想要计算一系列52张牌的permutationIndex
。 给定的排列索引一对一地映射到一系列卡片。 既然有52个! 52张卡的可能安排,你需要至少226位,或29字节。 那么,你的permutationIndex
已经非常大了!
由于您的排列索引已经是29个字节长,因此一些额外的字节不会产生很大的差异并使解决方案变得更容易。
例如,您可以将拉丁字母的每个字母映射到卡片。 鉴于我们有26个小写字母,26个大写字母,我们有52个字母可以代表52张卡片。
abcdefghijklm nopqrstuvwxyz ♥A234567890JQK♦A234567890JQK ABCDEFGHIJKLM NOPQRSTUVWXYZ ♣A234567890JQK♠A234567890JQK
现在你可以创建一个包含52个字母的字符串。 每个唯一的字母串代表52张卡的独特排列。 有了这个你可以:
- 生成随机字符串以获得随机排列。
- 通过查看给定位置的字母,立即找出卡片在哪里。
- 轻松随机播放,重新排序,插入和移除卡片。
字符串中的每个字符都表示为(在C#中)为16位Unicode值,但对于52个卡,您只需要6位。 所以你有更多的选择来选择一个表示:
- 832位,或104字节:52个Unicode字符的字符串
- 416位,或52字节:52字节的数组
- 320位或40字节:10个32位整数的数组,用于保存52 * 6位
- 312位或39字节:39字节的数组,用于保存52 * 6位
- 226位,或29个字节:绝对下限
表示3和4需要相当一些聪明的比特摆弄以获得特定卡的6位不在序列中。 我会推荐代表2,它保留了上面提到的大部分优点。
当您使用二进制表示而不是字符串表示时,您可以为每张卡创建一个具有唯一值的枚举,并使用:
public enum Cards : byte { HeartsAce HeartsTwo // ... HeartsTen HeartsJack HeartsQueen HeartsKing DiamondsAce DiamondsTwo // ... SpadesTen SpadesJack SpadesQueen SpadesKing }
你有一个非常有效的c#例子,在这个非常古老的post中, 对于n阶的第k个排列 (又名PermutationIndex):
对于那些对组合主题感兴趣的人:
- http://msdn.microsoft.com/en-us/magazine/cc163957.aspx
- http://msdn.microsoft.com/en-us/library/Aa289166(VS.71).aspx
在进入具体实施之前,我建议你仔细阅读。
和其他人一样,我不确定你想要做什么,但是如果你想在通信卡的通信/存储上节省尽可能多的空间,我会做以下事情:
我会使用带有flag属性的枚举来存储在单个Long上处理的卡片,因此我可以使用按位比较来查看已经处理了哪个卡片。
因为每张卡片都是一个单独的“旗帜”,其唯一编号设置为2的指数,所以它们永远不会发生冲突。
总的来说,即使您处理所有卡,存储仍将是8个字节。 你需要的任何额外数据,你可以在最后。
请参阅下面的工作示例。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication12 { class Program { static void Main(string[] args) { // Because each card is unique you could use Flag attributed Enum see the enum below and set each item a unique value I used 2 to the power of 52 Cards cardsdealt = Cards.Clubs_10 | Cards.Clubs_2 | Cards.Diamonds_3; if ((cardsdealt & Cards.Clubs_10) == Cards.Clubs_10) { Console.WriteLine("Card.Clubs_10 was dealt"); } // Storage would always be 8 bytes for the long data type } [Flags] public enum Cards : long { Spades_Ace = 1, Spades_2 = 2, Spades_3 = 4, Spades_4 = 8, Spades_5 = 16, Spades_6 = 32, Spades_7 = 64, Spades_8 = 128, Spades_9 = 256, Spades_10 = 512, Spades_Jack = 1024, Spades_Queen = 2048, Spades_King = 4096, Hearts_Ace = 8192, Hearts_2 = 16384, Hearts_3 = 32768, Hearts_4 = 65536, Hearts_5 = 131072, Hearts_6 = 262144, Hearts_7 = 524288, Hearts_8 = 1048576, Hearts_9 = 2097152, Hearts_10 = 4194304, Hearts_Jack = 8388608, Hearts_Queen = 16777216, Hearts_King = 33554432, Diamonds_Ace = 67108864, Diamonds_2 = 134217728, Diamonds_3 = 268435456, Diamonds_4 = 536870912, Diamonds_5 = 1073741824, Diamonds_6 = 2147483648, Diamonds_7 = 4294967296, Diamonds_8 = 8589934592, Diamonds_9 = 17179869184, Diamonds_10 = 34359738368, Diamonds_Jack = 68719476736, Diamonds_Queen = 137438953472, Diamonds_King = 274877906944, Clubs_Ace = 549755813888, Clubs_2 = 1099511627776, Clubs_3 = 2199023255552, Clubs_4 = 4398046511104, Clubs_5 = 8796093022208, Clubs_6 = 17592186044416, Clubs_7 = 35184372088832, Clubs_8 = 70368744177664, Clubs_9 = 140737488355328, Clubs_10 = 281474976710656, Clubs_Jack = 562949953421312, Clubs_Queen = 1125899906842620, Clubs_King = 2251799813685250, } } }