Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 随机选择设定位的有效方法_C#_Algorithm_Random_Montecarlo_Bitmask - Fatal编程技术网

C# 随机选择设定位的有效方法

C# 随机选择设定位的有效方法,c#,algorithm,random,montecarlo,bitmask,C#,Algorithm,Random,Montecarlo,Bitmask,我目前的爱好项目提供了蒙特卡罗模拟纸牌游戏的法式牌组52张,从2张到王牌 为了尽可能快地模拟,我使用在某些点中将多张卡表示为位掩码。下面是一些简化的代码: 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, F

我目前的爱好项目提供了蒙特卡罗模拟纸牌游戏的法式牌组52张,从2张到王牌

为了尽可能快地模拟,我使用在某些点中将多张卡表示为位掩码。下面是一些简化的代码:

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;
    }

}
我想从第二个struct CardPool中随机抽取一张或多张卡,但我无法想象如果不在池中迭代单个位,如何做到这一点。 是否有任何已知的算法来执行此操作?如果没有,你有没有想过要这么快

更新: 该结构并非用于从中抽取所有卡片。它经常被克隆,克隆数组是不可能的。我真的想到了绘制一张或多张卡的位操作

更新2: 我写了一个类,它把卡片作为列表来比较

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毫秒

承认:差别没有我想象的那么大。 但是考虑到我另外放弃了预先计算值的简单查找,这会产生很大的不同。 当然,用这个类随机抽取一张卡片会更快,但是我必须计算一个索引以便查找,这会把问题转移到另一个地方

我重复我最初的问题:有没有一个已知的算法可以从整数值中选择一个随机集位,或者有人有没有一个想法可以比迭代所有位更快地完成这项工作

关于使用带有列表或数组的类的讨论毫无意义,这不是我的问题,如果使用类会更好,我可以自己详细说明

更新3,查找代码:


代码已删除:这可能会产生误导,因为它没有提及线程主题会影响性能的段落。

由于同一张卡不能连续抽取两次,您可以将您案例中的每张卡、池的设置位索引放入一个数组中,将其洗牌,然后从阵列的任意一端逐个弹出卡

这是一个伪代码,因为我不懂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位整数中获得一次随机集位,使用来自的代码可以获得具有给定秩数的更重要集位的位的位置

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,那么您需要从52位数字中获取并删除一个随机集位。您想连续多次这样做,还是在两次绘制之间以另一种方式更改数字?还有,为什么不制作一个最多52张牌的阵列呢?这将更实用。数组将更实用,这是正确的,但性能较差,此外,我使用ulong作为字典的索引来查找预先计算的值。不一定要在相同的过程中删除,但是的,我想从52位数字中获取一个随机位。理想的连续工作。在你的情况下,绩效是一个真正合理的考虑因素吗?或者你只是在学习位运算?那么,你的池中只有5、6或7张卡的值是156742040?你比较了全甲板的复印时间。这是误导。如果你从来没有复制过完整的组块,那么就不要使用完整的组块进行配置。这是一个可以接受的解决方案。但在克隆之前,它并不打算从池中抽取所有的牌。我希望抽1到最多10张牌。因此,克隆时整个过程都会丢失。好吧,它可能仍然比每次迭代每个设置位要快。同样,这取决于您执行每个操作的频率。此时您所能做的就是分析。否则,就您当前的设计而言,您基本上只能使用这两种解决方案以及Yves。经过进一步研究,发现有一种快速方法可以获得一个随机集位,可能比迭代每一位都要快。这就是我要寻找的。谢谢。这比迭代所有位快9倍,比从现成的数组中绘制要慢2.5倍。遗憾的是,我还没有让它正常工作,最终的数字有时会超过最大值。