Random Number Generation
Generating random INDArrays, probability distributions, seeding, and reproducibility in ND4J
ND4J provides a flexible random number generation (RNG) system that covers everything from simple uniform arrays to parameterised probability distributions. The same API works on CPU and GPU: the backend automatically dispatches to the correct native RNG kernel.
Basic Random Arrays
The two most common entry points are Nd4j.rand and Nd4j.randn. Both accept a varargs shape and return a FLOAT array by default.
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
// Uniform random values in [0, 1) — shape 3 x 4
INDArray u = Nd4j.rand(3, 4);
// Standard normal values N(0, 1) — shape 5 x 5
INDArray n = Nd4j.randn(5, 5);
// 1D arrays
INDArray u1d = Nd4j.rand(100);
INDArray n1d = Nd4j.randn(100);rand draws from the continuous uniform distribution on the half-open interval [0, 1). randn draws from the standard normal distribution with mean 0 and standard deviation 1.
Typed Random Arrays
Pass a DataType as the first argument to control the element type. This is preferred in production code because it makes the precision explicit and avoids relying on the global default.
You can also pass a long[] shape if you have it in a variable:
Integer DataType values (INT32, INT64, etc.) are not supported by rand/randn; use a floating-point type and cast afterwards if you need integers.
Seeded Random for Reproducibility
For reproducible results — unit tests, debugging, paper experiments — set a seed on the global RNG instance before generating arrays.
Nd4j.getRandom() returns the Random instance associated with the calling thread (see Thread Safety below). Calling setSeed on it affects only subsequent calls on that thread.
You can also set a seed at JVM startup via the system property:
Distributions
For distributions beyond uniform and normal, use Nd4j.getDistributions() or construct a Distribution object directly and pass it to Nd4j.getExecutioner().exec(...). Distribution classes live in org.nd4j.linalg.api.rng.distribution and its sub-packages.
NormalDistribution
UniformDistribution
BinomialDistribution
The binomial distribution models the number of successes in n independent Bernoulli trials each with success probability p.
LogNormalDistribution
Samples drawn from log-normal are always positive, making it useful for modelling quantities that cannot be negative.
nextDouble on an INDArray
Nd4j.getRandom().nextDouble(INDArray, Distribution) fills an existing array in-place with samples from the given distribution and returns it:
This avoids a separate allocation when you already have a pre-allocated buffer.
Random Ops via the Executioner
For performance-critical code or GPU-friendly patterns, use the custom random ops available through Nd4j.getExecutioner(). These ops write directly into a provided output array without going through a Java Distribution wrapper.
BernoulliDistribution Op
Fills an array element-wise: each element is set to 1.0 with probability p and 0.0 with probability 1 - p.
GaussianDistribution Op
Fills an array with samples from N(mean, std). This is equivalent to Nd4j.randn but lets you specify mean and standard deviation.
UniformDistribution Op
Fills an array with samples from a uniform distribution on [low, high).
These ops respect the current thread's RNG seed, so setting Nd4j.getRandom().setSeed(...) before executing them yields reproducible results.
SameDiff Random Operations
SameDiff is ND4J's automatic-differentiation engine. Its random operations build computation graph nodes that are executed lazily. Use them when building models that require random noise as part of the forward pass (e.g., dropout, variational autoencoders).
The shape can also be supplied as a long[]:
To execute and retrieve the concrete value:
SameDiff random nodes participate fully in gradient computation. For example, a reparameterised sample z = mean + std * epsilon where epsilon is sd.random.normal(...) correctly propagates gradients through mean and std.
Setting a SameDiff Seed
Set the seed on the underlying ND4J RNG before calling outputSingle (or any execution method):
Thread Safety
ND4J maintains one Random instance per thread. Nd4j.getRandom() returns the instance for the calling thread. This design means:
Random generation is thread-safe by default: concurrent threads each draw from their own independent RNG state.
Seeding is per-thread: calling
setSeedon one thread does not affect any other thread's RNG.Parallel workloads (multi-threaded data loaders, parallel training) will each produce different sequences unless seeds are set independently on each thread.
To seed all threads identically at startup in a controlled test environment:
Using distinct seeds per thread rather than the same seed prevents correlated streams across threads, which can introduce bias in stochastic training.
Reproducibility
Getting fully reproducible results in deep learning involves several layers.
1. Set the ND4J seed early. Call Nd4j.getRandom().setSeed(seed) before any array creation, before building the model, and before the training loop.
2. Set the DL4J seed. When constructing a neural network, pass the same seed to the network configuration:
This controls weight initialisation inside DL4J layers, which internally calls back into the ND4J RNG.
3. Control framework-level parallelism. By default, ND4J may use multiple CPU threads for parallelised BLAS operations. The order of floating-point reductions can vary across runs even with the same seed. To force a single thread during debugging:
4. GPU non-determinism. CUDA operations such as atomics and certain convolution algorithms are non-deterministic at the hardware level. Enforcing full determinism on GPU is an active area of engineering. For experimentation, run on CPU to ensure bit-for-bit reproducibility.
5. Isolate randomness in tests. In JUnit tests, reset the seed in @BeforeEach:
This ensures each test case starts from the same RNG state regardless of test execution order.
Quick Reference
Array creation
Nd4j.rand(long...)
Uniform [0, 1)
Default FLOAT type
Nd4j.rand(DataType, long...)
Uniform [0, 1)
Explicit type
Nd4j.randn(long...)
Normal N(0, 1)
Default FLOAT type
Nd4j.randn(DataType, long...)
Normal N(0, 1)
Explicit type
Nd4j.rand(Distribution, long...)
Any Distribution
Parameterised
Distribution classes (org.nd4j.linalg.api.rng.distribution.impl)
NormalDistribution
mean, std
Gaussian distribution
UniformDistribution
low, high
Continuous uniform
BinomialDistribution
n, p
Binomial trials
LogNormalDistribution
mu, sigma
Log-normal; always positive
Random ops (org.nd4j.linalg.api.ops.random.impl)
BernoulliDistribution
output, p
Fill with 0/1 by probability
GaussianDistribution
output, mean, std
Fill with Gaussian samples
UniformDistribution
output, low, high
Fill with uniform samples
SameDiff random ops
sd.random.normal(name, mean, std, type, shape)
Normal N(mean, std)
sd.random.uniform(name, low, high, type, shape)
Uniform [low, high)
sd.random.bernoulli(name, p, type, shape)
Bernoulli 0/1
sd.random.truncatedNormal(name, mean, std, type, shape)
Truncated normal
For weight initialisation strategies that use these random primitives under the hood, see the DL4J layer configuration page. For memory management implications of allocating large random arrays, see the Memory and Workspaces page.
Last updated
Was this helpful?