|
1 | 1 | # cython: boundscheck=False |
2 | 2 | # cython: wraparound=False |
3 | 3 | # cython: initializedcheck=False |
| 4 | +# cython: cdivision=True |
4 | 5 |
|
5 | 6 | from libc.stdlib cimport calloc, free |
6 | 7 |
|
7 | | -import cython |
8 | | -import os |
| 8 | +import random |
9 | 9 |
|
10 | 10 | # Constants |
11 | | -cdef unsigned int MAX_VALUE = 10000 |
| 11 | +cdef const unsigned short MAX_VALUE = 10000 |
12 | 12 | # https://statmath.wu.ac.at/software/src/prng-3.0.2/doc/prng.html/Table_LCG.html |
13 | | -cdef unsigned int MODULOS = 134217689 |
14 | | -cdef unsigned int RANDOM_THRESHOLD = MODULOS - (MODULOS % MAX_VALUE) |
15 | | -cdef unsigned int LCG_MULTIPLIER = 3162696 |
16 | | -cdef bint* seen = <bint*>calloc(MAX_VALUE + 1, sizeof(bint)) # Bit array simulation |
17 | | - |
18 | | -cdef unsigned long seed = 0 |
| 13 | +cdef const unsigned int MODULOS = 134217689 |
| 14 | +cdef const unsigned long LCG_MULTIPLIER = 3162696 |
19 | 15 |
|
20 | | -cdef void _init_seed(): |
21 | | - global seed |
22 | | - cdef bytes random_bytes = os.urandom(4) |
23 | | - seed = int.from_bytes(random_bytes, byteorder='little') % MODULOS |
24 | | - |
25 | | - if seed == 0: |
26 | | - seed = 1 # Ensure seed is never zero to avoid low number cycle |
| 16 | +cdef unsigned int RANDOM_THRESHOLD = MODULOS - (MODULOS % MAX_VALUE) |
| 17 | +cdef char* seen = <char*>calloc(MAX_VALUE + 1, sizeof(char)) # Bit array simulation |
27 | 18 |
|
28 | | -_init_seed() |
| 19 | +cdef unsigned int seed = random.randint(1, MODULOS-1) |
29 | 20 |
|
30 | | -@cython.boundscheck(False) |
31 | | -@cython.wraparound(False) |
32 | 21 | cdef inline unsigned int _next_random() noexcept nogil: |
33 | 22 | """Generate a pseudo-random number based on a linear congruential generator""" |
34 | 23 | global seed |
35 | 24 |
|
36 | | - cdef unsigned long next_val = (<unsigned long>LCG_MULTIPLIER * seed) % MODULOS |
| 25 | + cdef unsigned int next_val = (LCG_MULTIPLIER * seed) % MODULOS |
37 | 26 |
|
38 | 27 | while next_val >= RANDOM_THRESHOLD: |
39 | | - next_val = (next_val * LCG_MULTIPLIER) % MODULOS |
| 28 | + next_val = (LCG_MULTIPLIER * next_val) % MODULOS |
40 | 29 |
|
41 | 30 | seed = next_val |
42 | 31 | return seed |
43 | 32 |
|
44 | | -@cython.boundscheck(False) |
45 | | -@cython.wraparound(False) |
46 | | -cpdef unsigned int random_id() noexcept nogil: |
| 33 | +cpdef unsigned short random_id() noexcept nogil: |
47 | 34 | """Generate a pseudo-random number in range [1, MAX_VALUE]""" |
48 | 35 | return 1 + (_next_random() % MAX_VALUE) |
49 | 36 |
|
50 | | -@cython.boundscheck(False) |
51 | | -@cython.wraparound(False) |
52 | | -cpdef list[unsigned int] random_unique_ids(int n): |
| 37 | +cpdef list[unsigned short] random_unique_ids(unsigned short n): |
53 | 38 | """Generate n unique random IDs in range[1, 10001]""" |
54 | | - cdef list[int] result = [0] * n |
55 | | - cdef int candidate, count = 0 |
| 39 | + cdef list[unsigned short] result = [0] * n |
| 40 | + cdef unsigned short candidate, count = 0 |
56 | 41 |
|
57 | 42 | try: |
58 | 43 | while count < n: |
59 | | - candidate = random_id() |
| 44 | + candidate = 1 + (_next_random() % MAX_VALUE) |
60 | 45 |
|
61 | 46 | if seen[candidate] == 0: # Not seen before |
62 | 47 | seen[candidate] = 1 |
|
0 commit comments