随机选择设置位的有效方法

马丁交易所

我当前的业余项目是为带有法国套牌的纸牌游戏(52张纸牌,从2到A)提供蒙特卡洛模拟

为了尽可能快地进行仿真,我在某些位置使用多张卡表示为位掩码。这是一些(简化的)代码:

public struct Card
{
    public enum CardColor : byte { Diamonds = 0, Hearts = 1, Spades = 2, Clubs = 3 }
    public enum CardValue : byte { Two = 0, Three = 1, Four = 2, Five = 3, Six = 4, Seven = 5, Eight = 6, Nine = 7, Ten = 8, Jack = 9, Queen = 10, King = 11, Ace = 12 }

    public CardColor Color { get; private set; }
    public CardValue Value { get; private set; }

    // ID provides a unique value for each card, ranging from 0 to 51, from 2Diamonds to AceClubs
    public byte ID { get { return (byte)(((byte)this.Value * 4) + (byte)this.Color); } }

    // --- Constructors ---
    public Card(CardColor color, CardValue value)
    {
        this.Color = color;
        this.Value = value;
    }
    public Card(byte id)
    {
        this.Color = (CardColor)(id % 4);
        this.Value = (CardValue)((id - (byte)this.Color) / 4);
    }
}

将多张卡作为位掩码的结构:

 public struct CardPool
 {
    private const ulong FULL_POOL = 4503599627370495;

    internal ulong Pool { get; private set; } // Holds all cards as set bit at Card.ID position

    public int Count()
    {
        ulong i = this.Pool;
        i = i - ((i >> 1) & 0x5555555555555555);
        i = (i & 0x3333333333333333) + ((i >> 2) & 0x3333333333333333);
        return (int)((((i + (i >> 4)) & 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >> 56);
    }

    public CardPool Clone()
    {
        return new CardPool() { Pool = this.Pool };
    }

    public void Add(Card card)
    {
        Add(card.ID);
    }
    public void Add(byte cardID)
    {
        this.Pool = this.Pool | ((ulong)1 << cardID);
    }

    public void Remove(Card card)
    {
        Remove(card.ID);
    }
    public void Remove(byte cardID)
    {
        this.Pool = this.Pool & ~((ulong)1 << cardID);
    }

    public bool Contains(Card card)
    {
        ulong mask = ((ulong)1 << card.ID);
        return (this.Pool & mask) == mask;
    }

    // --- Constructor ---
    public CardPool(bool filled)
    {
        if (filled)
            this.Pool = FULL_POOL;
        else
            this.Pool = 0;
    }

}

我想从第二个结构CardPool中随机绘制一张或多张卡,但我无法想象如何在不迭代池中单个位的情况下做到这一点。有什么已知的算法可以执行此操作吗?如果没有,您是否有快速执行此操作的想法?

更新:该结构并非旨在提取所有卡片。它经常被克隆,克隆数组是没有选择的。我真的想到了绘制一张或多张卡的位操作。

Update2:我编写了一个类,将这些卡保存为列表以进行比较。

public class CardPoolClass
{
    private List<Card> Cards;
    public void Add(Card card)
    {
        this.Cards.Add(card);
    }

    public CardPoolClass Clone()
    {
        return new CardPoolClass(this.Cards);
    }

    public CardPoolClass()
    {
        this.Cards = new List<Card> { };
    }
    public CardPoolClass(List<Card> cards)
    {
        this.Cards = cards.ToList();
    }
}

比较整个平台的1.000.000克隆操作:-结构:17毫秒-类:73毫秒

承认:差异不像我想的那么大。但是考虑到我还放弃了对预先计算的值的轻松查找,因此产生了很大的不同。当然,用此类绘制一张随机卡片会更快,但是我必须计算一个索引才能进行查找,而这只是将问题转移到另一个地方。

我重复我的第一个问题:是否有一种已知的算法可以从整数值中选择一个随机置位,还是有人想过要比迭代所有位更快地做到这一点?

关于将类与List或Array一起使用的讨论无济于事,这不是我的问题,如果可以更好地使用类,我可以自行阐述。

Update3,查找代码:

删除代码:这可能会引起误解,因为它不是指性能受线程主题影响的段落。

神经性

由于同一张卡片无法连续两次绘制,因此您可以将每张卡片(在您的情况下,是Pool的设置位的索引)放入一个阵列中,将其洗牌,然后从该阵列的任一端逐一弹出。

这是一个伪代码(因为我不知道C#)。

declare cards as an array of indices

for each bit in Pool
    push its index into cards

shuffle cards

when a card needs to be drawn
    pop an index from cards
    look up the card with Card(byte id)

编辑

这是一种算法,它使用64位整数破解程序中的代码从64位整数中一次获取随机置位,以获取具有给定等级(更重要的置位位数)的位的位置。

ulong v = this.Pool;
// ulong a = (v & ~0UL/3) + ((v >> 1) & ~0UL/3);
ulong a = v - ((v >> 1) & ~0UL/3);
// ulong b = (a & ~0UL/5) + ((a >> 2) & ~0UL/5);
ulong b = (a & ~0UL/5) + ((a >> 2) & ~0UL/5);
// ulong c = (b & ~0UL/0x11) + ((b >> 4) & ~0UL/0x11);
ulong c = (b + (b >> 4)) & ~0UL/0x11;
// ulong d = (c & ~0UL/0x101) + ((c >> 8) & ~0UL/0x101);
ulong d = (c + (c >> 8)) & ~0UL/0x101;
ulong t = (d >> 32) + (d >> 48);

int bitCount = ((c * (~0UL / 0xff)) >> 56);
ulong r = Randomizer.Next(1, bitCount+1);

ulong s = 64;
// if (r > t) {s -= 32; r -= t;}
s -= ((t - r) & 256) >> 3; r -= (t & ((t - r) >> 8));
t = (d >> (s - 16)) & 0xff;
// if (r > t) {s -= 16; r -= t;}
s -= ((t - r) & 256) >> 4; r -= (t & ((t - r) >> 8));
t = (c >> (s - 8)) & 0xf;
// if (r > t) {s -= 8; r -= t;}
s -= ((t - r) & 256) >> 5; r -= (t & ((t - r) >> 8));
t = (b >> (s - 4)) & 0x7;
// if (r > t) {s -= 4; r -= t;}
s -= ((t - r) & 256) >> 6; r -= (t & ((t - r) >> 8));
t = (a >> (s - 2)) & 0x3;
// if (r > t) {s -= 2; r -= t;}
s -= ((t - r) & 256) >> 7; r -= (t & ((t - r) >> 8));
t = (v >> (s - 1)) & 0x1;
// if (r > t) s--;
s -= ((t - r) & 256) >> 8;
s--; // s is now the position of a random set bit in v

带注释的行使用分支创建了另一个版本。如果要比较两个版本,请取消注释这些行,并注释其后的行。

在原始代码中,最后一行是s = 65 - s,但是由于1 << cardID用于卡池操作,而且r还是随机的,因此s--给出正确的值。

唯一需要注意的是的零值v但是无论如何,在一个空的池上执行此代码将毫无意义。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将最高有效设置位以下的所有位归零的最有效方法是什么?

如何有效地随机播放位?

C:设置变量范围内所有位的最有效方法

Python-根据类型选择随机消息的最有效方法

从长(且合理)稀疏向量中选择随机元素的最有效方法是什么?

从列表或元组列表中随机选择对象对的哪种方法更有效?

根据长度从列表中选择随机元素的有效方法

在Boost Graph Library中选择给定顶点的随机输入或输出邻居的有效方法?

查找Java中最左/最右未设置位的索引的最有效方法是什么?

在一个位置或更低的位置计数设置位的有效方法是什么?

将n个连续位设置为1的最有效方法?

有效地选择随机数

一般选择有效的随机枚举值

选择功能的更有效方法

在C中设置最高有效位

快速找到64位整数中最高有效位和最低有效位的方法

将所有位从最低有效位翻转到最高有效最后 1 位值的最有效方法是什么?

在javascript中获取数字的最低有效位的最有效方法是什么?

选择/显示列有效时是否设置OR条件?

建立随机排列列表的最有效方法

在Scala中获取随机元素的有效方法?

生成半随机序列的有效方法

仅使用按位运算的有效方法

处理JavaScript中压缩位的最有效方法

访问整数的最低有效位的最快方法?

如何有效地并行设置位向量的位?

设置了权威位(或用于响应的其他位)的 DNS 查询是否被视为有效?

有效地随机排列单词序列的位

有什么明智的方法可以从位集中提取最低有效位?