Element k of a uniform-looking permutation of 1 .. m, addressed by its coordinates.
The permutation counterpart of pf_random_at: a pure function of (seed, m, k), computed in
constant time and constant memory, touching no array. Element k depends on no other element,
so a loop over k may be run in any order, on any number of threads, and gives the same
answer -- which is the property the whole module exists for, extended from draws to
permutations.
!$omp parallel do
do k = 1, m
perm(k) = pf_random_perm_at(seed, m, k) ! same result at any thread count
end do
The first n values are a uniform random n-subset of 1 .. m, so a subset needs no
separate machinery and no memory: ask for k = 1 .. n. Prefix consistency follows for free --
a size-3 subset is a prefix of a size-6 one from the same (seed, m) -- and a subset at
n == m is the permutation, rather than merely agreeing with it.
m and k share their kind (integer(int32) or integer(int64)) and the result follows
them; seed is always integer(int64). k outside [1, m] is clamped rather than reported,
the same convention draw_or_1 uses and for the same reason: this is pure elemental and has
no way to abort.
Uniformity comes in two grades, split at m = 20, and the split is visible in
pf_random_perm_algorithm.
For m <= 20 the result is exactly uniform over all m! permutations -- one exactly
uniform rank in [0, m!) composed with a bijection onto S_m, so this is a property of the
construction rather than a measurement. 20 is where it stops because 20! is the last
factorial an integer(int64) holds.
For m >= 21 a 64-bit seed cannot address m! permutations at all, so exact uniformity is
not available to any construction with this signature. What is measured instead is that the
result is indistinguishable from a uniform permutation under an all-cells chi-square, an
order-4 tuple statistic, a parity test over the alternating group, and fixed-point,
cycle-structure, position-uniformity, subset-membership and structural tests.
Consecutive m are independent, in both regimes. Asking for a permutation of 5 and one of
6 under the same seed gives two unrelated answers rather than two views of one draw.
pf_random_perm_at for integer(int32) population size and index.
The result is inside [1, m] by construction, so narrowing the int64 worker's answer is
exact -- the same argument pf_random_int_at_i32 rests on.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int64), | intent(in) | :: | seed |
the permutation family's seed |
||
| integer(kind=int32), | intent(in) | :: | m |
population size; the permutation is of |
||
| integer(kind=int32), | intent(in) | :: | k |
1-based position; clamped into |
element k of that permutation
pf_random_perm_at for integer(int64) population size and index.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer(kind=int64), | intent(in) | :: | seed |
the permutation family's seed |
||
| integer(kind=int64), | intent(in) | :: | m |
population size; the permutation is of |
||
| integer(kind=int64), | intent(in) | :: | k |
1-based position; clamped into |
element k of that permutation