- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在国际象棋引擎上工作了一段时间。为了改进引擎,我编写了一些代码,将国际象棋位置从内存加载到一些调谐器代码中。我的机器上大约有 1.85B fens,加起来达到 40Gb(每个位置 24B)。
加载后,我得到一个位置 vector :
struct Position{
std::bitset<8*24> bits{};
}
void main(){
std::vector<Position> positions{};
// mimic some data loading
for(int i = 0; i < 1.85e9; i++){
positions.push_back(Position{})
}
// ...
}
数据按以下方式组织:
The positions are taken from games where the positions are seperated by just a few moves. Usually about 40-50 consecutive moves come the same game / line and are therefor somewhat equal.
最终我会在一个批处理中读取 16384 个位置,理想情况下这些位置都不是来自同一个游戏。因此,我在使用数据之前进行了一些初步排序。
我目前的洗牌方法是这样的:
auto rng = std::default_random_engine {};
std::shuffle(std::begin(positions), std::end(positions), rng);
不幸的是,这需要一些时间(大约 1-2 分钟)。由于我不需要完美的洗牌,我假设存在一些更简单的洗牌。
我的第二个方法是:
for(int i = 0; i < positions.size(); i++){
std::swap(positions[i], positions[(i*16384) % positions.size()]);
}
这将确保在一个批处理中不会有来自同一游戏的位置,并且由 16384 个条目均匀分布。
我想知道是否有更简单、更快的解决方案。特别是考虑到模运算符需要安静的一些时钟周期。
我很高兴任何“微不足道”的解决方案。
问候语芬恩
最佳答案
需要做出权衡:将 a a std::vector<size_t>
洗牌的指数预计比改组 std::vector<Position>
更便宜在访问 Position
时以间接为代价s 通过打乱索引。实际上 std::iota
的 cppreference 示例正在沿着这条线做一些事情(它使用迭代器):
#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>
#include <random>
#include <vector>
int main()
{
std::list<int> l(10);
std::iota(l.begin(), l.end(), -4);
std::vector<std::list<int>::iterator> v(l.size());
std::iota(v.begin(), v.end(), l.begin());
std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});
std::cout << "Contents of the list: ";
for(auto n: l) std::cout << n << ' ';
std::cout << '\n';
std::cout << "Contents of the list, shuffled: ";
for(auto i: v) std::cout << *i << ' ';
std::cout << '\n';
}
不是直接打乱列表,而是打乱一个迭代器 vector (带有 std::vector
索引也可以)并且 std::shuffle
只需要交换迭代器 (/indices) 而不是成本更高的实际元素(在示例中,“交换成本高”的元素只是 int
s)。
对于 std::list
我不认为按顺序迭代或通过混洗迭代器迭代之间有很大区别。另一方面,对于 std::vector
我确实希望产生重大影响。因此,我会打乱索引,然后重新排列 vector 一次,然后进行分析以查看哪个表现更好。
PS:如评论中所述,std::shuffle
已经是洗牌一系列元素的最佳算法。但是,请注意它平均将每个元素交换两次(可能来自 cppreference 的实现):
for (diff_t i = n-1; i > 0; --i) {
using std::swap;
swap(first[i], first[D(g, param_t(0, i))]);
另一方面,打乱索引然后重新排列 vector 只需要复制/移动每个元素一次(当有额外内存可用时)。
关于c++ - 最快的 "trivial"洗牌 vector 的方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69972833/
我被要求编写一个递归代码来打印一个数组。一位 friend 向我展示了这段代码: include int i=0; void print(int A[], int n) { if(i voi
我正在尝试让 google 的示例(应用内购买 version 3)测试应用“TrivialDrive”正常工作,但遇到以下问题: 我点击“buy gas”按钮,出现以下屏幕: 此消息有两种可能的解释
我知道这是一个非常常见的主题,但尽管典型的 UB 很容易找到,但到目前为止我还没有找到这个变体。 因此,我尝试正式引入 Pixel 对象,同时避免数据的实际拷贝。 这有效吗? struct Pixel
当一个函数接受一个函数参数(或者一个类有一个函数槽)时,有两种方法可供选择: def foo(..., my_func=None, ...): ... if my_func:
你知道任何 git 命令的工具/开关可以帮助我防止冲突或加速 merge 吗? 目前我经常遇到这样的“琐碎的冲突”: ++>>>>>> 587f917... 我想这是由一些空格引起的。这很容易被人类解
我在国际象棋引擎上工作了一段时间。为了改进引擎,我编写了一些代码,将国际象棋位置从内存加载到一些调谐器代码中。我的机器上大约有 1.85B fens,加起来达到 40Gb(每个位置 24B)。 加载后
我想在 Spark 中加入两个数据集。这就是我所做的: Dataset data = spark.read().format("parquet").load("hdfs://path"); Datas
我在国际象棋引擎上工作了一段时间。为了改进引擎,我编写了一些代码,将国际象棋位置从内存加载到一些调谐器代码中。我的机器上大约有 1.85B fens,加起来达到 40Gb(每个位置 24B)。 加载后
根据我对标准的理解,普通析构函数是一种隐式声明的析构函数,其类只有基本和非静态成员以及普通析构函数。鉴于此定义的递归性,在我看来,唯一的“递归停止”条件是找到具有非隐式声明的析构函数(即用户声明的)的
在 C++0x 中,我想确定一个类是否简单/是否具有标准布局,以便我可以使用 memcpy()、memset() 等... 我应该如何使用 type_traits 实现下面的代码,这样我才能确认一个类
我有一个带有“日期”列的表,我想做一个执行以下操作的查询: 如果日期是 星期一 , 周二 , 周三 , 或 星期四 , 显示的日期应该上移 1 天,如 DATEADD(day, 1, [Date])
我有一个非常复杂的项目(大约 100 个模块),我想在它上面运行 mvn dependency:tree .它失败了,提示它无法解决的依赖关系。该项目否则编译得很好。所以我创建了我能想到的最基本的项目
我希望这会返回一个 Date 对象,表示从现在开始一小时后的时间: Calendar.current.date(byAdding: DateComponents(hour: 1), to: Date(
Blocksworld显然是自动化规划中的基准领域。 This domain consists of a set of blocks, a table and a robot hand. The bl
我是 Angular 新手,我想做一些重要的输入验证。 基本上我有一张 table 。每行包含三个文本输入。当用户输入任何文本输入时,我想检查该表是否至少包含一行具有三个非空白输入字段。如果是的话
我无法理解导致索引错误的原因,而不是寻找快速修复。但是,如果我的代码让您反感/非常无效,请告诉我。目标是生成由两个四位数的乘积产生的回文。 代码: for x in range(10000):
我有一些数据需要多个 Activity 操作。基本上有一个只读屏幕和多个编辑屏幕。 起初我考虑将数据作为字符串参数传递给 Intent,但如果用户在编辑字段后按下后退按钮,这些更改将会丢失。 那么在不
我会定义“平凡可 move ” Calling the move constructor (or the move assignment operator) is equivalent to memc
我只想 class Trivial t instance Trivial t 这在 Haskell 98 中当然没用,因为你可以忽略约束;但使用 ConstraintKinds,我们可以明确要求类型为
在 future 的 C++ 标准中,我们将拥有“平凡的可重定位性”的概念,这意味着我们可以简单地将字节从一个对象复制到未初始化的内存块,并简单地忽略/清零原始对象的字节。 这样,我们就模仿了 C 风
我是一名优秀的程序员,十分优秀!