Ste*_*anE 7 random algorithm shuffle
以下是事实.
在桥牌比赛中有4名球员,分别是北,南,东和西.
所有52张牌都由每张牌手处理13张牌.
有一个荣誉计数系统.Ace = 4分,King = 3分,Queen = 2分,Jack = 1分.
我正在制造一个有限制的"卡片经销商",例如你可能会说北方的手必须有5个黑桃和13到16个荣誉计数点,其余的手是随机的.
如何在不影响"随机性"的情况下以最佳方式完成此任务并获得有效代码?
我用C#和.Net编码,但是伪代码中的一些想法会很好!
由于有人已经提到了我的 Deal 3.1,我想指出我在该代码中所做的一些优化。
首先,为了获得最灵活的约束,我想为我的经销商添加一个完整的编程语言,以便您可以生成具有不同类型的评估器和规则的完整约束库。我将 Tcl 用于该语言,因为我已经在工作中学习它,并且在 1994 年 Deal 0.0 发布时,Tcl 是最容易嵌入 C 应用程序的语言。
其次,我需要约束语言运行得相当快。约束在循环的深处运行。我的经销商中有相当多的代码是对查找表等的一些优化。
最令人惊讶和最简单的优化之一是在检查该座位的约束之前不向该座位发牌。例如,如果您希望北匹配约束 A 和南匹配约束 B,那么您的约束代码是:
match constraint A to north
match constraint B to south
Run Code Online (Sandbox Code Playgroud)
然后只有当你到达第一行时,你才填写北手。如果失败,您将拒绝整个交易。如果通过,接下来填写南手并检查其约束。如果失败,则放弃整个交易。否则,完成交易并接受它。
我在做一些分析时发现了这种优化,并注意到大部分时间都花在了随机数生成器上。
有一种奇特的优化,在某些情况下可以工作,称为“智能堆叠”。
deal::input smartstack south balanced hcp 20 21
Run Code Online (Sandbox Code Playgroud)
这会为南手生成一个“工厂”,它需要一些时间来建造,但它可以很快地填充一只手以符合这个标准。由于条件概率问题,智能堆叠一次只能应用于一手牌。[*]
智能堆叠采用“形状类” - 在这种情况下,“平衡”,“保持评估器”,在这种情况下,“hcp”,以及保持评估器的一系列值。“持牌评估者”是应用于每个花色然后合计的任何评估者,因此 hcp、controls、losers 和 hcp_plus_shape 等都是持牌评估者。
为了使智能堆叠有效,控股评估者需要采用相当有限的一组值。智能堆叠如何工作?这可能比我在这里发布的时间要多一些,但它基本上是一组巨大的表格。
最后一条评论:如果你真的只想要这个程序用于投标练习,而不是用于模拟,那么很多优化可能是不必要的。那是因为练习的本质使得它不值得花时间练习极其罕见的出价。因此,如果您遇到的情况仅在十亿次交易中出现一次,您可能真的不想担心。:)
[编辑:添加智能堆叠细节。]
好的,一套西装正好有 8192=2^13 种可能的持股。按长度和荣誉数对它们进行分组:
Holdings(length,points) = { set of holdings with this length and honor count }
Run Code Online (Sandbox Code Playgroud)
所以
Holdings(3,7) = {AK2, AK3,...,AKT,AQJ}
Run Code Online (Sandbox Code Playgroud)
然后让
h(length,points) = |Holdings(length,points)|
Run Code Online (Sandbox Code Playgroud)
现在列出与您的形状条件匹配的所有形状(黑桃 = 5):
5-8-0-0
5-7-1-0
5-7-0-1
...
5-0-0-8
Run Code Online (Sandbox Code Playgroud)
请注意,所有可能的手形的集合大小为 560,因此此列表并不大。
对于每个形状,通过列出每件套装的荣誉点数,列出获得您正在寻找的总荣誉点数的方法。例如,
Shape Points per suit
5-4-4-0 10-3-0-0
5-4-4-0 10-2-1-0
5-4-4-0 10-1-2-0
5-4-4-0 10-0-3-0
5-4-4-0 9-4-0-0
...
Run Code Online (Sandbox Code Playgroud)
使用我们的集合 Holdings(length,points),我们可以计算获得这些行的方法的数量。例如,对于第 5-4-4-0 10-3-0-0 行,您有:
h(5,10)*h(4,3)*h(4,0)*h(0,0)
Run Code Online (Sandbox Code Playgroud)
因此,随机选择这些行中的一个,根据计数的相对概率,然后,对于每个花色,从正确的 Holdings() 集合中随机选择一个控股。
显然,手形和点的范围越广,您需要预先计算的行就越多。多一点代码,你仍然可以用一些预先确定的牌来做到这一点 - 如果你知道黑桃 A 或 West 的整手牌或其他什么。
[*] 理论上,您可以用多手牌解决智能堆叠的这些条件概率问题,但该问题的解决方案只会使其仅对极其罕见的交易类型有效。那是因为工厂表中的行数大致是一只手堆叠的行数乘以另一只手堆叠的行数的乘积。此外,h() 表必须以将 n 张牌分为手 1、手 2 和其他手的方式数量为键,这将值的数量从大约 2^13 更改为 3^13 个可能值,这大约大两个数量级。
根据您的计算机的速度,执行以下操作可能就足够了:
与所有性能问题一样,要做的就是尝试一下看看!
编辑我尝试了一下并看到:
done 1000000 hands in 12914 ms, 4424 ok
Run Code Online (Sandbox Code Playgroud)
这没有考虑任何优化 - 它每秒产生 342 手牌,符合您的标准“北有 5 黑桃和 13-16 荣誉点”。我不知道你的申请的详细信息,但在我看来这可能就足够了。