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

2024-01-17

我当前的爱好项目为纸牌游戏提供蒙特卡罗模拟French牌组(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,但我无法想象如何在不迭代池中单个位的情况下做到这一点。 是否有任何已知的算法可以执行此操作?如果没有,你有办法快速做到这一点吗?

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

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

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

我重复我最初的问题:是否有一种已知的算法用于从整数值中选择随机设置位,或者是否有人有比迭代所有位更快地完成此操作的想法?

关于使用带有列表或数组的类的讨论没有任何意义,这不是我的问题,我可以自己详细说明是否使用类会更好。

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)

Edit

下面是一个在 64 位整数中获取随机设置位的算法,使用以下代码:位摆弄黑客 https://graphics.stanford.edu/~seander/bithacks.html获取具有给定等级的位的位置(更有效的设置位的数量)。

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。但在空池上执行这段代码无论如何都是没有意义的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

随机选择设置位的有效方法 的相关文章

  • 在动态事件处理程序中引用“this”

    在我的 myClass 类中 我使用 Reflection Emit 为 myClass 类成员之一动态编写事件处理程序 我已经成功地做到了这一点 现在 我想修改事件处理程序以调用 myClass 类中的实例方法之一 但是 我无法弄清楚如何
  • 使用API​​隐藏程序标题栏

    它可以使用 c 和 windows api 删除窗口控制台标题栏 如果是的话如何 请 这个简单的应用程序隐藏并显示其所在控制台的标题栏 它会立即将控制台标题更改为 guid 以查找窗口句柄 然后 它使用 ToggleTitleBar 使用找
  • 使用 OpenGL 着色器进行数学计算 (C++)

    我有一个矩阵 例如 100x100 尺寸 我需要对每个元素进行计算 matrix i j tt 8 5例如 我有一个巨大的矩阵 我想使用 OpenGL 着色器来实现该算法 我想使用着色器 例如 uniform float val unifo
  • C# 中四舍五入到偶数

    我没有看到 Math Round 的预期结果 return Math Round 99 96535789 2 MidpointRounding ToEven returning 99 97 据我了解 MidpointRounding ToE
  • 找到的程序集的清单定义与程序集引用不匹配

    我试图在 C Windows 窗体应用程序 Visual Studio 2005 中运行一些单元测试 但出现以下错误 System IO FileLoadException 无法加载文件或程序集 实用程序 版本 1 2 0 200 文化 中
  • Paradox 表 - Oledb 异常:外部表不是预期的格式

    我正在使用 Oledb 从 Paradox 表中读取一些数据 我遇到的问题是 当我将代码复制到控制台应用程序时 代码可以工作 但在 WinForms 中却不行 两者都以 x86 进行调试 我实际上只是复制代码 在 WinForms 应用程序
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • 指向字节数组的指针

    由于 Misra C 的要求 我的一位同事想要使用指针声明 但我遇到了一些问题 Misra 安全关键指南 不会让我们纯粹的程序员使用指针 但会让我们对数组字节进行操作 他打算获取一个指向字节数组的指针 因此我们不会在堆栈上传递实际的数组 T
  • 手动将 ClientBase 集合类型从 Array[] 更改为 List<>

    我将自己的 WCF 代理与 Client Base 一起使用 我想做一些类似于 svc util 中的 ct 属性的操作 并告诉代理返回 List 集合类型 我不能使用 List 因为实体由 nhibernate 管理 所以我必须使用 IL
  • 无法加载文件或程序集“EntityFramework,版本=6.0.0.0”

    我究竟做错了什么 我该如何解决这个问题 我有一个包含多个项目的解决方案 它是一个 MVC NET 4 5 Web 应用程序 在调试模式下启动后调用其中一个项目时 出现此错误 导致此错误的项目具有以下参考 两个都是版本6 0 0 0 应用程序
  • 如何用 C 语言练习 Unix 编程?

    经过五年的专业 Java 以及较小程度上的 Python 编程并慢慢感觉到我的计算机科学教育逐渐消失 我决定要拓宽我的视野 对世界的一般用处 并做一些 对我来说 感觉更重要的事情就像我真的对机器有影响一样 我选择学习 C 和 Unix 编程
  • 允许使用什么类型的内容作为 C 预处理器宏的参数?

    老实说 我很了解 C 编程语言的语法 但对 C 预处理器的语法几乎一无所知 尽管我有时在编程实践中使用它 所以问题来了 假设我们有一个简单的宏 它扩展为空 define macro param 可以放入宏调用构造中的语法有哪些限制 调用宏时
  • 错误左值需要作为赋值C++的左操作数

    整个程序基本上只允许用户移动光标 如果用户位于给定的坐标范围 2 2 内 则允许用户键入输入 我刚刚提供了一些我认为足以解决问题的代码 我不知道是什么导致了这个问题 你能解释一下为什么会发生吗 void goToXY int int 创建一
  • 将非算术类型作为参数传递给 cmath 函数是否有效?

    给定以下用户定义类型S具有转换功能double struct S operator double return 1 0 以及以下调用cmath http en cppreference com w cpp header cmath使用类型的
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • 使用(linq to sql)更新错误

    我有两个表 通过外键 CarrierID 绑定 Carrier CarrierID CarrierName CarrierID 1 CarrierName DHL CarrierID 2 CarrierName Fedex Vendor V
  • 纯虚函数可能没有内联定义。为什么?

    纯虚函数是那些虚函数并且具有纯说明符 0 第 10 4 条第 2 款C 03 的内容告诉我们什么是抽象类 顺便说一句 如下 注意 函数声明不能 同时提供纯说明符和定义 尾注 示例 struct C virtual void f 0 ill
  • 如何获取 QIcon 的文件/资源​​路径

    假设我做了这样的事情 QIcon myIcon resources icon ico 我稍后如何确定该图标的路径 例如 QString path myIcon getPath 问题是 没有getPath 会员 我找不到类似的东西 但肯定有办
  • g++ C++0x 枚举类编译器警告

    我一直在将可怕的 C 类型安全伪枚举重构为新的 C 0x 类型安全枚举 因为它们是way更具可读性 不管怎样 我在导出的类中使用它们 所以我明确地将它们标记为导出 enum class attribute visibility defaul
  • FindAsync 很慢,但是延迟加载很快

    在我的代码中 我曾经使用加载相关实体await FindAsync 希望我能更好地遵守 C 异步指南 var activeTemplate await exec DbContext FormTemplates FindAsync exec

随机推荐

  • setTextIsSelectable 如何防止键盘出现?

    如果我使用包含单个 EditText 的单个 Activity 创建一个简单的应用程序 并且我这样做 EditText editText EditText findViewById R id editText editText setTex
  • 禁用帆中的用户挂钩

    我正在 Heroku 上托管的一个项目中使用 sails 我有一个运行 sails Web 服务器的 Web 进程和一个使用与 Web 服务器使用的模型相同的模型的工作进程 为了使其成为可能 我有不同的方式使用相同的代码启动每个进程 app
  • Python 3.3 - 连接 Oracle 数据库

    python 3 3有连接Oracle数据库的模块吗 哪个最容易使用 像 mysql 模块之类的东西只能与 Oracle 一起使用 最好是 10g 版本 但 11g 也可以 有 cx Oracle Install gt You should
  • Java 内存模型:创建最终实例字段的循环引用图(所有字段均在同一线程内分配)是否安全?

    比我更了解 Java 内存模型的人可以确认我对以下代码正确同步的理解吗 class Foo private final Bar bar Foo this bar new Bar this class Bar private final Fo
  • Powershell函数递归获取元数据

    我正在修改一些 powershell 代码 我发现这些代码可以递归地从文件中获取元数据 但我在访问文件夹中的文件夹时遇到问题 我从 share 下直接列出的文件夹中获取元数据 而不是从那里的文件夹和文件中获取元数据 如何将此代码修改为文件夹
  • 为什么 notebook() 对 IJulia 不起作用?

    我在尝试使用时收到此问题notebook 在 Julia 命令行界面 REPL 中 julia gt using IJulia Info Precompiling IJulia 7073ff75 c697 5162 941a fcdaad2
  • 如何在没有太多引号的情况下序列化 JsonObject?

    我正在 com google gson JsonObject 上编写一个小型流畅的包装器 当我序列化 Json 时 我得到 key1 value1 key2 value2 key3 innerKey value3 如何去掉多余的引号 My
  • Microsoft Translator Text API 打破了 notranslate 跨度

    我正在使用 Microsoft Translator Text API 来翻译一些句子 我的句子包含一些我不需要翻译的文本部分 为了实现这一点 我使用 span class notranslate span 通过包装不可翻译的文本 在大多数
  • iOS 使用 AVAssetWriter 捕获视频时如何正确处理方向

    我正在制作一个利用 AVFoundation 录制视频的示例应用程序 重点是这样我可以更好地控制视频的录制方式 在我的示例项目中 我进行了视频捕获 但在正确处理方向方面遇到了困难 我在网络上进行了大量搜索 发现其他人建议我不应允许我的捕获视
  • long.Parse() C#

    在 C 中 如何将字符串 例如 100 100 转换为 long 我目前有一行代码是 long xi long Parse x System Globalization NumberStyles AllowThousands 但当 x 是
  • const 引用右值的类数据成员的生命周期是多少?

    一般来说 这个讨论仅取决于局部函数变量 void foo const int i use i till foo ends foo 3 但是 这条规则是否适用于class会员还 struct A const int a A a 3 versi
  • 实现 StickyGridHeaders Android 时标题中的按钮

    我正在尝试使用粘性网格标题 https github com TonicArtos StickyGridHeaders在我的 Android 应用程序中 它工作得很好 除非我尝试将点击监听器添加到 headerview 中的可点击 Imag
  • 使用 Eclipse 在 Android 虚拟机中启动 Android java 项目时出现问题

    我已经安装并设置了 Eclipse 和插件 ADT 以便与 Android SDK 一起使用 到目前为止 一切都很好 但是 当我尝试为我选择的任何 android 平台 例如 android 3 2 启动 VM 虚拟机 时 我只是将皮肤与键
  • Rapidjson C++ 释放对象内的数组

    我正在使用rapidjson C 库 https github com miloyip rapidjson 使用此库您可以创建 JSON 对象 目前我有一些记忆问题 情况 在我当前的设置中 我创建了一个新对象 并向其添加了值成员和数组成员
  • 将引导日期时间选择器与 Vuejs 2 结合使用

    我想将日期时间选择器与 vue 2 或 webpack 集成 我尝试搜索但找不到相关文章 有没有人将日期时间选择器与Vue2或webpack集成 有任何示例代码可供参考吗 任何帮助将不胜感激 Thanks 只需谷歌搜索 您肯定可以找到有关该
  • 如何在 Linux 2.6.35 上从用户模式清除和无效 ARM v7 处理器缓存

    我尝试清除指令行的 ARM v7 处理器缓存并使之无效 因为指令代码在执行中可能会发生变化 为了达到效果 我尝试了两种变体 他们来了 我用过海湾合作委员会 清除缓存 函数 但没有给出所需的结果 缓存中的指令代码没有改变 我寻找了 GCC 的
  • CountDownLatch 是否受到虚假唤醒的影响?

    诸如等待 通知和锁定 条件之类的并发管理机制似乎受到以下因素的影响虚假唤醒 https en wikipedia org wiki Spurious wakeup 开发人员通过重新检查情况是否确实发生变化来应对这些意外的唤醒 当谈到 Cou
  • 如何使用 Sql Profiler 捕获 SqlBulkCopy 中传递的数据?

    我一直在使用 Sql Profiler 来捕获 SQL 语句并重新运行有问题的语句 很有用 但是 有些代码使用 SqlBulkCopy API 我不知道如何捕获这些代码 我看到临时表的创建 但没有填充它们 似乎 SqlBulkCopy 绕过
  • Activity 的实例什么时候会消亡?

    这是一个示例代码 让我有点想念 package com leak import android app Activity import android app ProgressDialog import android os AsyncTa
  • 随机选择设置位的有效方法

    我当前的爱好项目为纸牌游戏提供蒙特卡罗模拟French牌组 52 张牌 从 2 到 A 为了尽可能快地进行模拟 我在某些地方将多张卡表示为位掩码 这是一些 简化的 代码 public struct Card public enum Card