The Fastest String Shuffle

Правка en2, от Gornak40, 2025-08-14 00:47:03

Imagine you need to iterate through many random permutations of a string and perform some quick check for each one. The check is so fast that the bottleneck in your solution becomes the shuffling. For simplicity, in the snippets, I've sped up the check to a no-op and added printing the final string.

Implementation with random_shuffle

#include <bits/stdc++.h>
using namespace std;

#define ITER 10000

int main() {
    string s; cin >> s;
    for (int i = 0; i < ITER; ++i) {
        random_shuffle(s.begin(), s.end());
        // do_something(s);
    }
    cout << s << '\n';
}

We'll benchmark this on a random [a-zA-Z0-9] string of length $$$2 \cdot 10^5$$$.

How to generate such a string on Unix?

Compile the code with g++ 14.2.1 using the standard -O2 flag common in most judging systems. On my laptop, the user CPU time was 26.78 seconds.

Implementation with shuffle

Perhaps you've read the well-known article about random or noticed the compiler warning. Indeed, no one uses random_shuffle anymore, as there's a modern replacement. How will it perform in the benchmark?

#include <bits/stdc++.h>
using namespace std;

#define ITER 10000

int main() {
    string s; cin >> s;
    mt19937 rng(42); // reproducibility
    for (int i = 0; i < ITER; ++i) {
        shuffle(s.begin(), s.end(), rng);
        // do_something(s);
    }
    cout << s << '\n';
}

The user CPU runtime for this implementation under the same conditions was $$$7.73$$$ seconds. Cool! In this note, we won't dive into implementation details or analyze the reason for this speedup.

Implementation with strfry

Let's turn to C's legacy. The standard library sometimes contains useful functions, like strtok, which have no modern C++ equivalents. And if you look under _GNU_SOURCE, you can find even more surprising things.

Suddenly, [glibc](https://www.gnu.org/software/libc enters the stage. For our task, strfry can help. This function takes a null-terminated string and shuffles it. The global state of the PRNG remains unchanged, and the permutation will vary between runs. Under the hood, the function uses getrandom in the standard linear swap algorithm. By the way, according to the C++11 standard, our string is null-terminated.

#include <bits/stdc++.h>
using namespace std;

#define ITER 10000

int main() {
    string s; cin >> s;
    for (int i = 0; i < ITER; ++i) {
        strfry(s.data());
        // do_something(s);
    }
    cout << s << '\n';
}

The user CPU time under the same conditions was $$$5.54$$$ seconds. Hooray, we've found the winner!

Теги random, random_shuffle, gnu, c++, c language, benchmarks

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
ru9 Русский Gornak40 2025-08-14 01:03:52 0 (опубликовано)
en11 Английский Gornak40 2025-08-14 01:03:37 0 (published)
en10 Английский Gornak40 2025-08-14 01:01:19 79
en9 Английский Gornak40 2025-08-14 01:00:33 79 Tiny change: 'he winner!' -> 'he winner!\n\n![Chart](https://mirror.codeforces.com/aac5f4/chart.png)'
en8 Английский Gornak40 2025-08-14 00:54:39 1 Tiny change: 'Imagine yo' -> ' Imagine yo'
en7 Английский Gornak40 2025-08-14 00:52:52 3 Tiny change: ' user CPU runtime for t' -> ' user CPU time for t'
en6 Английский Gornak40 2025-08-14 00:51:52 15 Tiny change: ' seconds. Hooray, we've fo' -> ' seconds. Congratulations, we've fo'
en5 Английский Gornak40 2025-08-14 00:50:10 1 Tiny change: 'tware/libc enters th' -> 'tware/libc) enters th'
en4 Английский Gornak40 2025-08-14 00:49:41 15 Tiny change: 'd library sometimes contains useful fu' -> 'd library contains some useful fu'
en3 Английский Gornak40 2025-08-14 00:47:54 2 Tiny change: ' time was 26.78 seconds.\' -> ' time was $26.78$ seconds.\'
en2 Английский Gornak40 2025-08-14 00:47:03 1062
ru8 Русский Gornak40 2025-08-14 00:44:52 6
en1 Английский Gornak40 2025-08-14 00:43:42 3176 Initial revision for English translation (saved to drafts)
ru7 Русский Gornak40 2025-08-14 00:31:11 127
ru6 Русский Gornak40 2025-08-14 00:23:36 143
ru5 Русский Gornak40 2025-08-14 00:20:38 11 Мелкая правка: 'и добавил вывод финальной' -> 'и добавил печать финальной'
ru4 Русский Gornak40 2025-08-14 00:20:11 160
ru3 Русский Gornak40 2025-08-14 00:14:13 2065 Мелкая правка: 'ень просто: `tr -dc '[' -> 'ень просто.\n`tr -dc '['
ru2 Русский Gornak40 2025-08-13 23:22:31 26 Мелкая правка: 'troka.txt`\n' -> 'troka.txt`.\n'
ru1 Русский Gornak40 2025-08-13 23:21:22 794 Первая редакция (сохранено в черновиках)