Random and unique are different properties

A random draw answers, “Which candidate did this call pick?” A uniqueness rule answers, “Can this candidate appear again?” If every draw starts from the full candidate pool, an earlier result is still eligible on the next call. A duplicate is therefore allowed even when the generator is working as designed.

Keep the model separate from real telephone numbers. In the calculation below, N is a declared pool of distinct, approved test-fixture tokens, not the size of a national numbering plan and not a count of available phone lines. A phone-shaped string does not establish assignment or reachability.

A developer on Stack Overflow asked how to generate a unique ten-digit phone number while avoiding a leading zero and checking for duplicates before inserting it into a table. That public question shows one concrete requirement; it does not tell us how frequent the need is or whether any particular generated value is safe to contact.

Estimate collision risk for a random batch

Assume each draw is independent, uniformly distributed across N fixture tokens, and made with replacement. The first draw cannot collide. The second avoids the first with probability 1 − 1/N; the third avoids both earlier draws with probability 1 − 2/N. Multiplying the no-collision chances and subtracting from one gives:

Collision probability = 1 − ∏i=0k−1(1 − i/N)

These examples use only an abstract token pool. The values are calculated from the formula above; they are not measurements of phone-number availability.

Distinct fixture tokens in pool (N)Independent draws (k)Chance of at least one duplicateInterpretation
100,0001004.83%A collision is possible even in a small batch.
100,00030036.17%Plan for retries or sample without replacement.
100,0001,00099.33%A duplicate is overwhelmingly likely under this model.
1,000,0001,00039.33%A larger pool lowers risk but does not guarantee uniqueness.

The calculation depends on the assumptions. A weighted or biased generator can collide more often, while sampling without replacement changes the result: a correctly implemented sampler returns distinct pool entries for every request with k ≤ N. When k > N, it must report that the requested batch cannot be made from the available pool.

Choose the scope of the uniqueness promise

RequirementSuitable designFailure to test
Duplicates are acceptable in a load testDraw with replacement and record that repeats are allowed.Verify downstream code tolerates repeated fixture keys.
Every item in one returned batch must differSample without replacement from a pool of unique canonical fixture keys.Request more items than the pool contains and expect an explicit error.
No duplicates across requests or workersReserve keys in shared storage with an atomic operation and a unique constraint.Run concurrent reservations for the same candidate; only one may succeed.
Repeat the same failing test laterRecord the seed, pool version, ordering, and generator version.Confirm replay is stable without treating the seed as a uniqueness guarantee.

State the scope in the API contract. “Unique in this response” is weaker than “never used by any job.” A process-local set can protect one request, but it cannot coordinate another process, container, or region. If uniqueness spans workers, let the shared store make the final decision atomically.

Sample without replacement from a safe fixture pool

For a batch that must be unique, choose distinct fixture keys without putting each selected item back. This partial Fisher–Yates shuffle does at most count picks. Its input pool must already contain unique keys, and the supplied integer function must return a uniform integer from zero up to, but not including, its argument.

function sampleFixtures(pool, count, randomInt) {
  if (!Number.isSafeInteger(count) || count < 0) {
    throw new RangeError("count must be a non-negative integer");
  }
  if (new Set(pool).size !== pool.length) {
    throw new Error("fixture pool contains duplicate keys");
  }
  if (count > pool.length) {
    throw new RangeError("requested batch exceeds fixture pool capacity");
  }

  const items = [...pool];
  for (let i = 0; i < count; i += 1) {
    const remaining = items.length - i;
    const offset = randomInt(remaining);
    if (!Number.isInteger(offset) || offset < 0 || offset >= remaining) {
      throw new RangeError("randomInt returned an out-of-range value");
    }
    const j = i + offset;
    [items[i], items[j]] = [items[j], items[i]];
  }
  return items.slice(0, count);
}

const batch = sampleFixtures(
  approvedFixtureKeys,
  requestedCount,
  limit => seededRng.int(limit)
);

Here the pool contains approved fixture keys, not arbitrary dialable numbers. If each key maps to a formatted phone value, also test uniqueness on the canonical value your application stores. Two different display strings can normalize to the same value, so comparing raw text alone can miss a duplicate.

Python’s official documentation makes the same distinction: random.choices draws with replacement, while random.sample selects unique population entries without replacement. Use the equivalent operation in your language and verify the exact contract of its random-integer function.

Make cross-job uniqueness atomic

For persistent allocations, an in-memory set is only a local cache. Two workers can read the same unused candidate before either writes it. Store the canonical fixture key under a database uniqueness constraint and claim it in one atomic insert or transaction. If the insert loses a race, draw another candidate and retry only up to a fixed limit.

When the retry limit is reached, return a capacity or conflict error. Do not loop forever, silently return fewer fixtures than requested, or fall back to a live numbering range. Keep the database rule responsible for correctness; a pre-insert “does this exist?” check is useful for reducing retries but cannot prevent a concurrent race by itself.

Document whether uniqueness lasts for one response, one test run, or the lifetime of a dataset. Expiration and reuse are product rules: if old fixture keys can be recycled, make that boundary explicit and test it.

Test the duplicate path on purpose

A happy-path test that happens to return different values does not prove the generator can prevent collisions. Inject a deterministic random source that selects the same pool position twice, then verify that the contract does what it promises.

  • For a unique batch of size k, assert exactly k results and k distinct canonical keys.
  • For k > N, assert a clear capacity error rather than a partial batch.
  • Force a repeated draw and verify that the uniqueness layer rejects or skips it according to the API contract.
  • Send concurrent requests for one remaining key and confirm the shared unique constraint allows only one reservation.
  • Normalize equivalent display forms before comparing keys; do not use formatting differences to disguise duplicates.
  • For seeded replay, pin the fixture-pool version and ordering as well as the seed.

Keep fixture data synthetic and separate from live SMS delivery. A unique value can still be assigned to someone; uniqueness says nothing about ownership or reachability. See the SMS safety guide for transport boundaries.

Common questions

Does random mean that every phone number will be different?

No. A random draw may select the same candidate more than once. Distinct results require a no-replacement sampler, a deduplication rule, or an atomic uniqueness constraint, depending on the scope.

How do I generate a unique phone-number batch?

Choose from a declared pool of unique, approved fixture keys without replacement. Reject a request that exceeds the pool size, and compare canonical values if keys are formatted into phone strings. A Stack Overflow developer asked a closely related question about unique generated numbers and duplicate checks; the linked thread is a user question, not authoritative numbering guidance.

Will using the same seed prevent duplicates?

No. A seed can make a pseudo-random sequence repeatable, which helps reproduce a test. It does not change sampling with replacement into sampling without replacement and does not coordinate separate workers.

References and related guides