2,540 karma · joined November 22, 2016
From a probabilistic standpoint, even if it isn’t a perfect Faro shuffle, almost-Faro shuffling clearly introduces far fewer bits of entropy which is less than ideal.
The riffle shuffle also has to be one of the only things you actually get worse at the more you practice it.
Almost every “crazy” decision you’ve heard about in recent years comes from this one district.
template<typename T>
T my_construct() { T result; return result; }If you have backups, that’s one obvious solution, but people might not have uncorrupted backups.
I still think you should do it and emit a warning about it at the very least, but it’s not trivial to handle.
Additionally, your analysis only considers when the permutation is reachable. You would also want to look at how likely each permutation is.
Let's call the two splits L for left and R for right and consider the resulting deck to be defined as a sequence of these letters indicating whether that card was taken from the left or right pile so in theory you can specify a shuffle uniquely like "LRRLLLLLLRRR...". This method of labeling shuffles has the advantage of being extremely easy to record and also makes the bias I found apparent where there are way too many pairs of "LR" and "RL" compared to "LL" and "RR" like the "7 shuffles is enough" model suggests there should be.
For some reason, when I did all this analysis last year, I was mostly thinking about how to ensure my MTG decks are sufficiently shuffled. I didn't really think about the implications for card counting. However, normal riffle shuffling seems to have less bias (but still present) than the mash shuffling I looked at and I think most casinos use machines for shuffling these days.
For standard riffle shuffling, there is often a bias in pulling from the pile opposite from the last pulled card (so alternating pairs of left and right). The model saying “7 shuffles is sufficient” as people like to quote presumes you pull from a pile only proportional to the size of the pile (so you get many fewer alternating runs).
I thought it was a really cool idea and it seems totally viable for things like a game engine. All the pointer chasing would be expensive, but being able to freely allocate memory without much lag would be a really nice property, particularly for games with plugins like Roblox, Factorio, etc. where code quality is often out of your control.
Even if you want more control over your system, I still think technically capable people would be better served by having a separate administrator account from your normal day-to-day account which you have to explicitly log into (so no UAC prompts, you need to go onto that other account and then you get the UAC prompt). Unfortunately, I think most Desktop OSes are still too unusable with this sort of workflow due to how much software insists on admin for installation.
I've personally seen this with a game recently and my own ISP. I can ping their West Coast servers in 20-40ms while pinging their Chicago servers takes 200ms+.
This is actually one huge flaw with the current way IP is done. Because your ISP is closely tied with the last-mile of routing, you have very little control over optimizing those backend layers of routing.
This doesn't explain why you wouldn't just run a thin proxy at the edge and instead want to run a full PHP app. For that, my guess is this makes sense when you don't want to run a full server all the time or if you want it to be rapidly scalable.
struct my_bitfield
{
uint16_t b:5;
uint16_t g:6;
uint16_t r:5;
};
bool IsEveryComponentGreaterThanOrEqual4(my_bitfield x, my_bitfield y)
{
return x.b > y.b && x.g > y.b && x.r > y.r;
}bool IsEveryComponentGreaterThanOrEqual(uint16_t x, uint16_t y) { int rv; auto xr = x & 0xF100; auto yr = y & 0xF100; rv = xr >= yr;
auto xg = x & 0x07E0; auto yg = y & 0x07E0; rv &= xg >= yg;
auto xb = x & 0x001F; auto yb = y & 0x001F; rv &= xb >= yb;
return rv != 0; }
According to the Compiler Explorer, the one presented in the article still has far fewer instructions on x86_64. Interestingly, the optimization of eliminating the bitshifts doesn't change how it is compiled which says something about trusting your compiler for some of this stuff. https://godbolt.org/z/zWf5GnPPb
However, one more interesting thing is that the modified version I made is nearly identical in size on ARMv8 for the one in the article vs my version (12 vs 11 instructions). https://godbolt.org/z/d4TPqGhKe
One variant that I think might work even better than RAII or defer in a lot of languages is having a thread local "context" which you attach all cleanup actions to. It even works in C, you just define cleanup as a list of
typedef void(cleanup_function*)(void* context);
which is saved as into a thread local. Unlike RAII, you don't need to create a custom type for every cleanup action and unlike the call-with pattern from functional programming, lifetimes of these lists can be non-hierarchical.
However, I'm still glad to see defer being considered for C. It's a lot better than using goto for cleanup.