Suiji (随机 in Chinese, /suíjī/, meaning random) is a high efficient random number generator in Typst. Partial algorithm is inherited from GSL and most APIs are similar to NumPy Random Generator. It provides pure function implementation and does not rely on any global state variables, resulting in better performance and independency.
Features
- All functions are immutable, which means results of random are completely deterministic.
- Core random engine chooses “Maximally equidistributed combined Tausworthe generator” and “LCG”.
- Generate random integers or floats from various distribution.
- Randomly shuffle an array of objects.
- Randomly sample from an array of objects.
- Generate blind text of Simplified Chinese.
Examples
The example below uses suiji
and cetz
packages to create a trajectory of a random walk.
#import "@preview/suiji:0.3.0": *
#import "@preview/cetz:0.2.2"
#set page(width: auto, height: auto, margin: 0.5cm)
#cetz.canvas(length: 5pt, {
import cetz.draw: *
let n = 2000
let (x, y) = (0, 0)
let (x-new, y-new) = (0, 0)
let rng = gen-rng(42)
let v = ()
for i in range(n) {
(rng, v) = uniform(rng, low: -2.0, high: 2.0, size: 2)
(x-new, y-new) = (x - v.at(1), y - v.at(0))
let col = color.mix((blue.transparentize(20%), 1-i/n), (green.transparentize(20%), i/n))
line(stroke: (paint: col, cap: "round", thickness: 2pt),
(x, y), (x-new, y-new)
)
(x, y) = (x-new, y-new)
}
})
Another example is drawing the the famous Matrix rain effect of falling green characters in a terminal.
#import "@preview/suiji:0.3.0": *
#import "@preview/cetz:0.2.2"
#set page(width: auto, height: auto, margin: 0pt)
#cetz.canvas(length: 1pt, {
import cetz.draw: *
let font-size = 10
let num-col = 80
let num-row = 32
let text-len = 16
let seq = "abcdefghijklmnopqrstuvwxyz!@#$%^&*".split("").slice(1, 35).map(it => raw(it))
let rng = gen-rng(42)
let num-cnt = 0
let val = 0
let chars = ()
rect((-10, -10), (font-size * (num-col - 1) * 0.6 + 10, font-size * (num-row - 1) + 10), fill: black)
for c in range(num-col) {
(rng, num-cnt) = integers(rng, low: 1, high: 3)
for cnt in range(num-cnt) {
(rng, val) = integers(rng, low: -10, high: num-row - 2)
(rng, chars) = choice(rng, seq, size: text-len)
for i in range(text-len) {
let y = i + val
if y >= 0 and y < num-row {
let col = green.transparentize((i / text-len) * 100%)
content(
(c * font-size * 0.6, y * font-size),
text(size: font-size * 1pt, fill:col, stroke: (text-len - i) * 0.04pt + col, chars.at(i))
)
}
}
}
}
})
Usage
Import suiji
module first before use any random functions from it.
#import "@preview/suiji:0.3.0": *
For functions that generate various random numbers or randomly shuffle, a random number generator object (rng) is required as both input and output arguments. And the original rng should be created by function gen-rng
, with an integer as the argument of seed. This calling style seems to be a little inconvenient, as it is limited by the programming paradigm. For function discrete
, the given probalilities of the discrete events should be preprocessed by function discrete-preproc
, whose output serves as an input argument of discrete
.
Another set of functions with the same functionality provides higher performance (about 3 times faster) and has the suffix -f
in their names. For example, gen-rng-f
and integers-f
are the fast versions of gen-rng
and integers
, respectively.
The function rand-sc
creates blind text of Simplified Chinese. This function yields a Chinese-like Lorem Ipsum blind text with the given number of words, where punctuations are optional.
The code below generates several random permutations of 0 to 9. Each time after function shuffle-f
is called, the value of variable rng
is updated, so generated permutations are different.
#{
let rng = gen-rng-f(42)
let a = ()
for i in range(5) {
(rng, a) = shuffle-f(rng, range(10))
[#(a.map(it => str(it)).join(" ")) \ ]
}
}
For more codes with these functions see tests.
Reference
gen-rng
/ gen-rng-f
Construct a new random number generator with a seed.
#let gen-rng(seed) = {...}
-
Input Arguments
seed
: [int
] value of seed.
-
Output Arguments
rng
: [object
] generated object of random number generator.
randi-f
Return a raw random integer from [0, 2^31).
#let randi-f(rng) = {...}
-
Input Arguments
rng
: [object
|int
] object of random number generator (generated by function*-f
).
-
Output Arguments
rng-out
: [object
|int
] updated object of random number generator (random integer from the interval [0, 2^31-1]).
integers
/ integers-f
Return random integers from low
(inclusive) to high
(exclusive).
#let integers(rng, low: 0, high: 100, size: none, endpoint: false) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.low
: [int
] lowest (signed) integers to be drawn from the distribution, optional.high
: [int
] one above the largest (signed) integer to be drawn from the distribution, optional.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.endpoint
: [bool
] if true, sample from the interval [low
,high
] instead of the default [low
,high
), optional.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [int
|array
ofint
] array of random numbers.
- [
random
/ random-f
Return random floats in the half-open interval [0.0, 1.0).
#let random(rng, size: none) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [float
|array
offloat
] array of random numbers.
- [
uniform
/ uniform-f
Draw samples from a uniform distribution. Samples are uniformly distributed over the half-open interval [low
, high
) (includes low
, but excludes high
).
#let uniform(rng, low: 0.0, high: 1.0, size: none) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.low
: [float
] lower boundary of the output interval, optional.high
: [float
] upper boundary of the output interval, optional.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [float
|array
offloat
] array of random numbers.
- [
normal
/ normal-f
Draw random samples from a normal (Gaussian) distribution.
#let normal(rng, loc: 0.0, scale: 1.0, size: none) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.loc
: [float
] mean (centre) of the distribution, optional.scale
: [float
] standard deviation (spread or width) of the distribution, must be non-negative, optional.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [float
|array
offloat
] array of random numbers.
- [
discrete-preproc
and discrete
/ discrete-preproc-f
and discrete-f
Return random indices from the given probalilities of the discrete events.
#let discrete-preproc(p) = {...}
-
Input Arguments
p
: [array
ofint
orfloat
] the array of probalilities of the discrete events, probalilities must be non-negative.
-
Output Arguments
g
: [object
] generated object that contains the lookup table.
#let discrete(rng, g, size: none) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.g
: [object
] generated object that contains the lookup table bydiscrete-preproc
function.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [int
|array
ofint
] array of random indices.
- [
shuffle
/ shuffle-f
Randomly shuffle a given array.
#let shuffle(rng, arr) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.arr
: [array
] the array to be shuffled.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [array
] shuffled array.
- [
choice
/ choice-f
Generate random samples from a given array.
#let choice(rng, arr, size: none, replacement: true, permutation: true) = {...}
-
Input Arguments
rng
: [object
] object of random number generator.arr
: [array
] the array to be sampled.size
: [none
orint
] returned array size, must be none or non-negative integer, optional.replacement
: [bool
] whether the sample is with or without replacement, optional; default is true, meaning that a value ofarr
can be selected multiple times.permutation
: [bool
] whether the sample is permuted when sampling without replacement, optional; default is true, false provides a speedup.
-
Output Arguments
- [
array
] : (rng-out
,arr-out
)rng-out
: [object
] updated object of random number generator.arr-out
: [array
] generated random samples.
- [
rand-sc
Generate blind text of Simplified Chinese.
#let rand-sc(words, seed: 42, punctuation: false, gap: 10) = {...}
-
Input Arguments
words
: [int
] the length of the blind text in pure words.seed
: [int
] value of seed, optional.punctuation
: [bool
] if true, insert punctuations in generated words, optional.gap
: [int
] average gap between punctuations, optional.
-
Output Arguments
- [
str
] : generated blind text of Simplified Chinese.
- [